roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {
69                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
70                     window.addEventListener('touchstart', function __set_has_touch__ () {
71                         Roo.isTouch = true;
72                         window.removeEventListener('touchstart', __set_has_touch__);
73                     });
74                     return false; // no touch on chrome!?
75                 }
76                 document.createEvent("TouchEvent");  
77                 return true;  
78             } catch (e) {  
79                 return false;  
80             } 
81             
82         })();
83     // remove css image flicker
84         if(isIE && !isIE7){
85         try{
86             document.execCommand("BackgroundImageCache", false, true);
87         }catch(e){}
88     }
89     
90     Roo.apply(Roo, {
91         /**
92          * True if the browser is in strict mode
93          * @type Boolean
94          */
95         isStrict : isStrict,
96         /**
97          * True if the page is running over SSL
98          * @type Boolean
99          */
100         isSecure : isSecure,
101         /**
102          * True when the document is fully initialized and ready for action
103          * @type Boolean
104          */
105         isReady : false,
106         /**
107          * Turn on debugging output (currently only the factory uses this)
108          * @type Boolean
109          */
110         
111         debug: false,
112
113         /**
114          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
115          * @type Boolean
116          */
117         enableGarbageCollector : true,
118
119         /**
120          * True to automatically purge event listeners after uncaching an element (defaults to false).
121          * Note: this only happens if enableGarbageCollector is true.
122          * @type Boolean
123          */
124         enableListenerCollection:false,
125
126         /**
127          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
128          * the IE insecure content warning (defaults to javascript:false).
129          * @type String
130          */
131         SSL_SECURE_URL : "javascript:false",
132
133         /**
134          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
135          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
136          * @type String
137          */
138         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
139
140         emptyFn : function(){},
141         
142         /**
143          * Copies all the properties of config to obj if they don't already exist.
144          * @param {Object} obj The receiver of the properties
145          * @param {Object} config The source of the properties
146          * @return {Object} returns obj
147          */
148         applyIf : function(o, c){
149             if(o && c){
150                 for(var p in c){
151                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
152                 }
153             }
154             return o;
155         },
156
157         /**
158          * Applies event listeners to elements by selectors when the document is ready.
159          * The event name is specified with an @ suffix.
160 <pre><code>
161 Roo.addBehaviors({
162    // add a listener for click on all anchors in element with id foo
163    '#foo a@click' : function(e, t){
164        // do something
165    },
166
167    // add the same listener to multiple selectors (separated by comma BEFORE the @)
168    '#foo a, #bar span.some-class@mouseover' : function(){
169        // do something
170    }
171 });
172 </code></pre>
173          * @param {Object} obj The list of behaviors to apply
174          */
175         addBehaviors : function(o){
176             if(!Roo.isReady){
177                 Roo.onReady(function(){
178                     Roo.addBehaviors(o);
179                 });
180                 return;
181             }
182             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
183             for(var b in o){
184                 var parts = b.split('@');
185                 if(parts[1]){ // for Object prototype breakers
186                     var s = parts[0];
187                     if(!cache[s]){
188                         cache[s] = Roo.select(s);
189                     }
190                     cache[s].on(parts[1], o[b]);
191                 }
192             }
193             cache = null;
194         },
195
196         /**
197          * Generates unique ids. If the element already has an id, it is unchanged
198          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
199          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
200          * @return {String} The generated Id.
201          */
202         id : function(el, prefix){
203             prefix = prefix || "roo-gen";
204             el = Roo.getDom(el);
205             var id = prefix + (++idSeed);
206             return el ? (el.id ? el.id : (el.id = id)) : id;
207         },
208          
209        
210         /**
211          * Extends one class with another class and optionally overrides members with the passed literal. This class
212          * also adds the function "override()" to the class that can be used to override
213          * members on an instance.
214          * @param {Object} subclass The class inheriting the functionality
215          * @param {Object} superclass The class being extended
216          * @param {Object} overrides (optional) A literal with members
217          * @method extend
218          */
219         extend : function(){
220             // inline overrides
221             var io = function(o){
222                 for(var m in o){
223                     this[m] = o[m];
224                 }
225             };
226             return function(sb, sp, overrides){
227                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
228                     overrides = sp;
229                     sp = sb;
230                     sb = function(){sp.apply(this, arguments);};
231                 }
232                 var F = function(){}, sbp, spp = sp.prototype;
233                 F.prototype = spp;
234                 sbp = sb.prototype = new F();
235                 sbp.constructor=sb;
236                 sb.superclass=spp;
237                 
238                 if(spp.constructor == Object.prototype.constructor){
239                     spp.constructor=sp;
240                    
241                 }
242                 
243                 sb.override = function(o){
244                     Roo.override(sb, o);
245                 };
246                 sbp.override = io;
247                 Roo.override(sb, overrides);
248                 return sb;
249             };
250         }(),
251
252         /**
253          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
254          * Usage:<pre><code>
255 Roo.override(MyClass, {
256     newMethod1: function(){
257         // etc.
258     },
259     newMethod2: function(foo){
260         // etc.
261     }
262 });
263  </code></pre>
264          * @param {Object} origclass The class to override
265          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
266          * containing one or more methods.
267          * @method override
268          */
269         override : function(origclass, overrides){
270             if(overrides){
271                 var p = origclass.prototype;
272                 for(var method in overrides){
273                     p[method] = overrides[method];
274                 }
275             }
276         },
277         /**
278          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
279          * <pre><code>
280 Roo.namespace('Company', 'Company.data');
281 Company.Widget = function() { ... }
282 Company.data.CustomStore = function(config) { ... }
283 </code></pre>
284          * @param {String} namespace1
285          * @param {String} namespace2
286          * @param {String} etc
287          * @method namespace
288          */
289         namespace : function(){
290             var a=arguments, o=null, i, j, d, rt;
291             for (i=0; i<a.length; ++i) {
292                 d=a[i].split(".");
293                 rt = d[0];
294                 /** eval:var:o */
295                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
296                 for (j=1; j<d.length; ++j) {
297                     o[d[j]]=o[d[j]] || {};
298                     o=o[d[j]];
299                 }
300             }
301         },
302         /**
303          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
304          * <pre><code>
305 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
306 Roo.factory(conf, Roo.data);
307 </code></pre>
308          * @param {String} classname
309          * @param {String} namespace (optional)
310          * @method factory
311          */
312          
313         factory : function(c, ns)
314         {
315             // no xtype, no ns or c.xns - or forced off by c.xns
316             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
317                 return c;
318             }
319             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
320             if (c.constructor == ns[c.xtype]) {// already created...
321                 return c;
322             }
323             if (ns[c.xtype]) {
324                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
325                 var ret = new ns[c.xtype](c);
326                 ret.xns = false;
327                 return ret;
328             }
329             c.xns = false; // prevent recursion..
330             return c;
331         },
332          /**
333          * Logs to console if it can.
334          *
335          * @param {String|Object} string
336          * @method log
337          */
338         log : function(s)
339         {
340             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
341                 return; // alerT?
342             }
343             console.log(s);
344             
345         },
346         /**
347          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
348          * @param {Object} o
349          * @return {String}
350          */
351         urlEncode : function(o){
352             if(!o){
353                 return "";
354             }
355             var buf = [];
356             for(var key in o){
357                 var ov = o[key], k = Roo.encodeURIComponent(key);
358                 var type = typeof ov;
359                 if(type == 'undefined'){
360                     buf.push(k, "=&");
361                 }else if(type != "function" && type != "object"){
362                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
363                 }else if(ov instanceof Array){
364                     if (ov.length) {
365                             for(var i = 0, len = ov.length; i < len; i++) {
366                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
367                             }
368                         } else {
369                             buf.push(k, "=&");
370                         }
371                 }
372             }
373             buf.pop();
374             return buf.join("");
375         },
376          /**
377          * Safe version of encodeURIComponent
378          * @param {String} data 
379          * @return {String} 
380          */
381         
382         encodeURIComponent : function (data)
383         {
384             try {
385                 return encodeURIComponent(data);
386             } catch(e) {} // should be an uri encode error.
387             
388             if (data == '' || data == null){
389                return '';
390             }
391             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
392             function nibble_to_hex(nibble){
393                 var chars = '0123456789ABCDEF';
394                 return chars.charAt(nibble);
395             }
396             data = data.toString();
397             var buffer = '';
398             for(var i=0; i<data.length; i++){
399                 var c = data.charCodeAt(i);
400                 var bs = new Array();
401                 if (c > 0x10000){
402                         // 4 bytes
403                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
404                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
405                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
406                     bs[3] = 0x80 | (c & 0x3F);
407                 }else if (c > 0x800){
408                          // 3 bytes
409                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
410                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
411                     bs[2] = 0x80 | (c & 0x3F);
412                 }else if (c > 0x80){
413                        // 2 bytes
414                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
415                     bs[1] = 0x80 | (c & 0x3F);
416                 }else{
417                         // 1 byte
418                     bs[0] = c;
419                 }
420                 for(var j=0; j<bs.length; j++){
421                     var b = bs[j];
422                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
423                             + nibble_to_hex(b &0x0F);
424                     buffer += '%'+hex;
425                }
426             }
427             return buffer;    
428              
429         },
430
431         /**
432          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
433          * @param {String} string
434          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
435          * @return {Object} A literal with members
436          */
437         urlDecode : function(string, overwrite){
438             if(!string || !string.length){
439                 return {};
440             }
441             var obj = {};
442             var pairs = string.split('&');
443             var pair, name, value;
444             for(var i = 0, len = pairs.length; i < len; i++){
445                 pair = pairs[i].split('=');
446                 name = decodeURIComponent(pair[0]);
447                 value = decodeURIComponent(pair[1]);
448                 if(overwrite !== true){
449                     if(typeof obj[name] == "undefined"){
450                         obj[name] = value;
451                     }else if(typeof obj[name] == "string"){
452                         obj[name] = [obj[name]];
453                         obj[name].push(value);
454                     }else{
455                         obj[name].push(value);
456                     }
457                 }else{
458                     obj[name] = value;
459                 }
460             }
461             return obj;
462         },
463
464         /**
465          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
466          * passed array is not really an array, your function is called once with it.
467          * The supplied function is called with (Object item, Number index, Array allItems).
468          * @param {Array/NodeList/Mixed} array
469          * @param {Function} fn
470          * @param {Object} scope
471          */
472         each : function(array, fn, scope){
473             if(typeof array.length == "undefined" || typeof array == "string"){
474                 array = [array];
475             }
476             for(var i = 0, len = array.length; i < len; i++){
477                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
478             }
479         },
480
481         // deprecated
482         combine : function(){
483             var as = arguments, l = as.length, r = [];
484             for(var i = 0; i < l; i++){
485                 var a = as[i];
486                 if(a instanceof Array){
487                     r = r.concat(a);
488                 }else if(a.length !== undefined && !a.substr){
489                     r = r.concat(Array.prototype.slice.call(a, 0));
490                 }else{
491                     r.push(a);
492                 }
493             }
494             return r;
495         },
496
497         /**
498          * Escapes the passed string for use in a regular expression
499          * @param {String} str
500          * @return {String}
501          */
502         escapeRe : function(s) {
503             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
504         },
505
506         // internal
507         callback : function(cb, scope, args, delay){
508             if(typeof cb == "function"){
509                 if(delay){
510                     cb.defer(delay, scope, args || []);
511                 }else{
512                     cb.apply(scope, args || []);
513                 }
514             }
515         },
516
517         /**
518          * Return the dom node for the passed string (id), dom node, or Roo.Element
519          * @param {String/HTMLElement/Roo.Element} el
520          * @return HTMLElement
521          */
522         getDom : function(el){
523             if(!el){
524                 return null;
525             }
526             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
527         },
528
529         /**
530         * Shorthand for {@link Roo.ComponentMgr#get}
531         * @param {String} id
532         * @return Roo.Component
533         */
534         getCmp : function(id){
535             return Roo.ComponentMgr.get(id);
536         },
537          
538         num : function(v, defaultValue){
539             if(typeof v != 'number'){
540                 return defaultValue;
541             }
542             return v;
543         },
544
545         destroy : function(){
546             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
547                 var as = a[i];
548                 if(as){
549                     if(as.dom){
550                         as.removeAllListeners();
551                         as.remove();
552                         continue;
553                     }
554                     if(typeof as.purgeListeners == 'function'){
555                         as.purgeListeners();
556                     }
557                     if(typeof as.destroy == 'function'){
558                         as.destroy();
559                     }
560                 }
561             }
562         },
563
564         // inpired by a similar function in mootools library
565         /**
566          * Returns the type of object that is passed in. If the object passed in is null or undefined it
567          * return false otherwise it returns one of the following values:<ul>
568          * <li><b>string</b>: If the object passed is a string</li>
569          * <li><b>number</b>: If the object passed is a number</li>
570          * <li><b>boolean</b>: If the object passed is a boolean value</li>
571          * <li><b>function</b>: If the object passed is a function reference</li>
572          * <li><b>object</b>: If the object passed is an object</li>
573          * <li><b>array</b>: If the object passed is an array</li>
574          * <li><b>regexp</b>: If the object passed is a regular expression</li>
575          * <li><b>element</b>: If the object passed is a DOM Element</li>
576          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
577          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
578          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
579          * @param {Mixed} object
580          * @return {String}
581          */
582         type : function(o){
583             if(o === undefined || o === null){
584                 return false;
585             }
586             if(o.htmlElement){
587                 return 'element';
588             }
589             var t = typeof o;
590             if(t == 'object' && o.nodeName) {
591                 switch(o.nodeType) {
592                     case 1: return 'element';
593                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
594                 }
595             }
596             if(t == 'object' || t == 'function') {
597                 switch(o.constructor) {
598                     case Array: return 'array';
599                     case RegExp: return 'regexp';
600                 }
601                 if(typeof o.length == 'number' && typeof o.item == 'function') {
602                     return 'nodelist';
603                 }
604             }
605             return t;
606         },
607
608         /**
609          * Returns true if the passed value is null, undefined or an empty string (optional).
610          * @param {Mixed} value The value to test
611          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
612          * @return {Boolean}
613          */
614         isEmpty : function(v, allowBlank){
615             return v === null || v === undefined || (!allowBlank ? v === '' : false);
616         },
617         
618         /** @type Boolean */
619         isOpera : isOpera,
620         /** @type Boolean */
621         isSafari : isSafari,
622         /** @type Boolean */
623         isFirefox : isFirefox,
624         /** @type Boolean */
625         isIE : isIE,
626         /** @type Boolean */
627         isIE7 : isIE7,
628         /** @type Boolean */
629         isIE11 : isIE11,
630         /** @type Boolean */
631         isGecko : isGecko,
632         /** @type Boolean */
633         isBorderBox : isBorderBox,
634         /** @type Boolean */
635         isWindows : isWindows,
636         /** @type Boolean */
637         isLinux : isLinux,
638         /** @type Boolean */
639         isMac : isMac,
640         /** @type Boolean */
641         isIOS : isIOS,
642         /** @type Boolean */
643         isTouch : isTouch,
644
645         /**
646          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
647          * you may want to set this to true.
648          * @type Boolean
649          */
650         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
651         
652         
653                 
654         /**
655          * Selects a single element as a Roo Element
656          * This is about as close as you can get to jQuery's $('do crazy stuff')
657          * @param {String} selector The selector/xpath query
658          * @param {Node} root (optional) The start of the query (defaults to document).
659          * @return {Roo.Element}
660          */
661         selectNode : function(selector, root) 
662         {
663             var node = Roo.DomQuery.selectNode(selector,root);
664             return node ? Roo.get(node) : new Roo.Element(false);
665         }
666         
667     });
668
669
670 })();
671
672 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
673                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
674                 "Roo.app", "Roo.ux",
675                 "Roo.bootstrap",
676                 "Roo.bootstrap.dash");
677 /*
678  * Based on:
679  * Ext JS Library 1.1.1
680  * Copyright(c) 2006-2007, Ext JS, LLC.
681  *
682  * Originally Released Under LGPL - original licence link has changed is not relivant.
683  *
684  * Fork - LGPL
685  * <script type="text/javascript">
686  */
687
688 (function() {    
689     // wrappedn so fnCleanup is not in global scope...
690     if(Roo.isIE) {
691         function fnCleanUp() {
692             var p = Function.prototype;
693             delete p.createSequence;
694             delete p.defer;
695             delete p.createDelegate;
696             delete p.createCallback;
697             delete p.createInterceptor;
698
699             window.detachEvent("onunload", fnCleanUp);
700         }
701         window.attachEvent("onunload", fnCleanUp);
702     }
703 })();
704
705
706 /**
707  * @class Function
708  * These functions are available on every Function object (any JavaScript function).
709  */
710 Roo.apply(Function.prototype, {
711      /**
712      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
713      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
714      * Will create a function that is bound to those 2 args.
715      * @return {Function} The new function
716     */
717     createCallback : function(/*args...*/){
718         // make args available, in function below
719         var args = arguments;
720         var method = this;
721         return function() {
722             return method.apply(window, args);
723         };
724     },
725
726     /**
727      * Creates a delegate (callback) that sets the scope to obj.
728      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
729      * Will create a function that is automatically scoped to this.
730      * @param {Object} obj (optional) The object for which the scope is set
731      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
732      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
733      *                                             if a number the args are inserted at the specified position
734      * @return {Function} The new function
735      */
736     createDelegate : function(obj, args, appendArgs){
737         var method = this;
738         return function() {
739             var callArgs = args || arguments;
740             if(appendArgs === true){
741                 callArgs = Array.prototype.slice.call(arguments, 0);
742                 callArgs = callArgs.concat(args);
743             }else if(typeof appendArgs == "number"){
744                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
745                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
746                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
747             }
748             return method.apply(obj || window, callArgs);
749         };
750     },
751
752     /**
753      * Calls this function after the number of millseconds specified.
754      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
755      * @param {Object} obj (optional) The object for which the scope is set
756      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
757      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
758      *                                             if a number the args are inserted at the specified position
759      * @return {Number} The timeout id that can be used with clearTimeout
760      */
761     defer : function(millis, obj, args, appendArgs){
762         var fn = this.createDelegate(obj, args, appendArgs);
763         if(millis){
764             return setTimeout(fn, millis);
765         }
766         fn();
767         return 0;
768     },
769     /**
770      * Create a combined function call sequence of the original function + the passed function.
771      * The resulting function returns the results of the original function.
772      * The passed fcn is called with the parameters of the original function
773      * @param {Function} fcn The function to sequence
774      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
775      * @return {Function} The new function
776      */
777     createSequence : function(fcn, scope){
778         if(typeof fcn != "function"){
779             return this;
780         }
781         var method = this;
782         return function() {
783             var retval = method.apply(this || window, arguments);
784             fcn.apply(scope || this || window, arguments);
785             return retval;
786         };
787     },
788
789     /**
790      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
791      * The resulting function returns the results of the original function.
792      * The passed fcn is called with the parameters of the original function.
793      * @addon
794      * @param {Function} fcn The function to call before the original
795      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
796      * @return {Function} The new function
797      */
798     createInterceptor : function(fcn, scope){
799         if(typeof fcn != "function"){
800             return this;
801         }
802         var method = this;
803         return function() {
804             fcn.target = this;
805             fcn.method = method;
806             if(fcn.apply(scope || this || window, arguments) === false){
807                 return;
808             }
809             return method.apply(this || window, arguments);
810         };
811     }
812 });
813 /*
814  * Based on:
815  * Ext JS Library 1.1.1
816  * Copyright(c) 2006-2007, Ext JS, LLC.
817  *
818  * Originally Released Under LGPL - original licence link has changed is not relivant.
819  *
820  * Fork - LGPL
821  * <script type="text/javascript">
822  */
823
824 Roo.applyIf(String, {
825     
826     /** @scope String */
827     
828     /**
829      * Escapes the passed string for ' and \
830      * @param {String} string The string to escape
831      * @return {String} The escaped string
832      * @static
833      */
834     escape : function(string) {
835         return string.replace(/('|\\)/g, "\\$1");
836     },
837
838     /**
839      * Pads the left side of a string with a specified character.  This is especially useful
840      * for normalizing number and date strings.  Example usage:
841      * <pre><code>
842 var s = String.leftPad('123', 5, '0');
843 // s now contains the string: '00123'
844 </code></pre>
845      * @param {String} string The original string
846      * @param {Number} size The total length of the output string
847      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
848      * @return {String} The padded string
849      * @static
850      */
851     leftPad : function (val, size, ch) {
852         var result = new String(val);
853         if(ch === null || ch === undefined || ch === '') {
854             ch = " ";
855         }
856         while (result.length < size) {
857             result = ch + result;
858         }
859         return result;
860     },
861
862     /**
863      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
864      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
865      * <pre><code>
866 var cls = 'my-class', text = 'Some text';
867 var s = String.format('<div class="{0}">{1}</div>', cls, text);
868 // s now contains the string: '<div class="my-class">Some text</div>'
869 </code></pre>
870      * @param {String} string The tokenized string to be formatted
871      * @param {String} value1 The value to replace token {0}
872      * @param {String} value2 Etc...
873      * @return {String} The formatted string
874      * @static
875      */
876     format : function(format){
877         var args = Array.prototype.slice.call(arguments, 1);
878         return format.replace(/\{(\d+)\}/g, function(m, i){
879             return Roo.util.Format.htmlEncode(args[i]);
880         });
881     }
882 });
883
884 /**
885  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
886  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
887  * they are already different, the first value passed in is returned.  Note that this method returns the new value
888  * but does not change the current string.
889  * <pre><code>
890 // alternate sort directions
891 sort = sort.toggle('ASC', 'DESC');
892
893 // instead of conditional logic:
894 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
895 </code></pre>
896  * @param {String} value The value to compare to the current string
897  * @param {String} other The new value to use if the string already equals the first value passed in
898  * @return {String} The new value
899  */
900  
901 String.prototype.toggle = function(value, other){
902     return this == value ? other : value;
903 };/*
904  * Based on:
905  * Ext JS Library 1.1.1
906  * Copyright(c) 2006-2007, Ext JS, LLC.
907  *
908  * Originally Released Under LGPL - original licence link has changed is not relivant.
909  *
910  * Fork - LGPL
911  * <script type="text/javascript">
912  */
913
914  /**
915  * @class Number
916  */
917 Roo.applyIf(Number.prototype, {
918     /**
919      * Checks whether or not the current number is within a desired range.  If the number is already within the
920      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
921      * exceeded.  Note that this method returns the constrained value but does not change the current number.
922      * @param {Number} min The minimum number in the range
923      * @param {Number} max The maximum number in the range
924      * @return {Number} The constrained value if outside the range, otherwise the current value
925      */
926     constrain : function(min, max){
927         return Math.min(Math.max(this, min), max);
928     }
929 });/*
930  * Based on:
931  * Ext JS Library 1.1.1
932  * Copyright(c) 2006-2007, Ext JS, LLC.
933  *
934  * Originally Released Under LGPL - original licence link has changed is not relivant.
935  *
936  * Fork - LGPL
937  * <script type="text/javascript">
938  */
939  /**
940  * @class Array
941  */
942 Roo.applyIf(Array.prototype, {
943     /**
944      * 
945      * Checks whether or not the specified object exists in the array.
946      * @param {Object} o The object to check for
947      * @return {Number} The index of o in the array (or -1 if it is not found)
948      */
949     indexOf : function(o){
950        for (var i = 0, len = this.length; i < len; i++){
951               if(this[i] == o) { return i; }
952        }
953            return -1;
954     },
955
956     /**
957      * Removes the specified object from the array.  If the object is not found nothing happens.
958      * @param {Object} o The object to remove
959      */
960     remove : function(o){
961        var index = this.indexOf(o);
962        if(index != -1){
963            this.splice(index, 1);
964        }
965     },
966     /**
967      * Map (JS 1.6 compatibility)
968      * @param {Function} function  to call
969      */
970     map : function(fun )
971     {
972         var len = this.length >>> 0;
973         if (typeof fun != "function") {
974             throw new TypeError();
975         }
976         var res = new Array(len);
977         var thisp = arguments[1];
978         for (var i = 0; i < len; i++)
979         {
980             if (i in this) {
981                 res[i] = fun.call(thisp, this[i], i, this);
982             }
983         }
984
985         return res;
986     }
987     
988 });
989
990
991  
992 /*
993  * Based on:
994  * Ext JS Library 1.1.1
995  * Copyright(c) 2006-2007, Ext JS, LLC.
996  *
997  * Originally Released Under LGPL - original licence link has changed is not relivant.
998  *
999  * Fork - LGPL
1000  * <script type="text/javascript">
1001  */
1002
1003 /**
1004  * @class Date
1005  *
1006  * The date parsing and format syntax is a subset of
1007  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1008  * supported will provide results equivalent to their PHP versions.
1009  *
1010  * Following is the list of all currently supported formats:
1011  *<pre>
1012 Sample date:
1013 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1014
1015 Format  Output      Description
1016 ------  ----------  --------------------------------------------------------------
1017   d      10         Day of the month, 2 digits with leading zeros
1018   D      Wed        A textual representation of a day, three letters
1019   j      10         Day of the month without leading zeros
1020   l      Wednesday  A full textual representation of the day of the week
1021   S      th         English ordinal day of month suffix, 2 chars (use with j)
1022   w      3          Numeric representation of the day of the week
1023   z      9          The julian date, or day of the year (0-365)
1024   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1025   F      January    A full textual representation of the month
1026   m      01         Numeric representation of a month, with leading zeros
1027   M      Jan        Month name abbreviation, three letters
1028   n      1          Numeric representation of a month, without leading zeros
1029   t      31         Number of days in the given month
1030   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1031   Y      2007       A full numeric representation of a year, 4 digits
1032   y      07         A two digit representation of a year
1033   a      pm         Lowercase Ante meridiem and Post meridiem
1034   A      PM         Uppercase Ante meridiem and Post meridiem
1035   g      3          12-hour format of an hour without leading zeros
1036   G      15         24-hour format of an hour without leading zeros
1037   h      03         12-hour format of an hour with leading zeros
1038   H      15         24-hour format of an hour with leading zeros
1039   i      05         Minutes with leading zeros
1040   s      01         Seconds, with leading zeros
1041   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1042   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1043   T      CST        Timezone setting of the machine running the code
1044   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1045 </pre>
1046  *
1047  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1048  * <pre><code>
1049 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1050 document.write(dt.format('Y-m-d'));                         //2007-01-10
1051 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1052 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1053  </code></pre>
1054  *
1055  * Here are some standard date/time patterns that you might find helpful.  They
1056  * are not part of the source of Date.js, but to use them you can simply copy this
1057  * block of code into any script that is included after Date.js and they will also become
1058  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1059  * <pre><code>
1060 Date.patterns = {
1061     ISO8601Long:"Y-m-d H:i:s",
1062     ISO8601Short:"Y-m-d",
1063     ShortDate: "n/j/Y",
1064     LongDate: "l, F d, Y",
1065     FullDateTime: "l, F d, Y g:i:s A",
1066     MonthDay: "F d",
1067     ShortTime: "g:i A",
1068     LongTime: "g:i:s A",
1069     SortableDateTime: "Y-m-d\\TH:i:s",
1070     UniversalSortableDateTime: "Y-m-d H:i:sO",
1071     YearMonth: "F, Y"
1072 };
1073 </code></pre>
1074  *
1075  * Example usage:
1076  * <pre><code>
1077 var dt = new Date();
1078 document.write(dt.format(Date.patterns.ShortDate));
1079  </code></pre>
1080  */
1081
1082 /*
1083  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1084  * They generate precompiled functions from date formats instead of parsing and
1085  * processing the pattern every time you format a date.  These functions are available
1086  * on every Date object (any javascript function).
1087  *
1088  * The original article and download are here:
1089  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1090  *
1091  */
1092  
1093  
1094  // was in core
1095 /**
1096  Returns the number of milliseconds between this date and date
1097  @param {Date} date (optional) Defaults to now
1098  @return {Number} The diff in milliseconds
1099  @member Date getElapsed
1100  */
1101 Date.prototype.getElapsed = function(date) {
1102         return Math.abs((date || new Date()).getTime()-this.getTime());
1103 };
1104 // was in date file..
1105
1106
1107 // private
1108 Date.parseFunctions = {count:0};
1109 // private
1110 Date.parseRegexes = [];
1111 // private
1112 Date.formatFunctions = {count:0};
1113
1114 // private
1115 Date.prototype.dateFormat = function(format) {
1116     if (Date.formatFunctions[format] == null) {
1117         Date.createNewFormat(format);
1118     }
1119     var func = Date.formatFunctions[format];
1120     return this[func]();
1121 };
1122
1123
1124 /**
1125  * Formats a date given the supplied format string
1126  * @param {String} format The format string
1127  * @return {String} The formatted date
1128  * @method
1129  */
1130 Date.prototype.format = Date.prototype.dateFormat;
1131
1132 // private
1133 Date.createNewFormat = function(format) {
1134     var funcName = "format" + Date.formatFunctions.count++;
1135     Date.formatFunctions[format] = funcName;
1136     var code = "Date.prototype." + funcName + " = function(){return ";
1137     var special = false;
1138     var ch = '';
1139     for (var i = 0; i < format.length; ++i) {
1140         ch = format.charAt(i);
1141         if (!special && ch == "\\") {
1142             special = true;
1143         }
1144         else if (special) {
1145             special = false;
1146             code += "'" + String.escape(ch) + "' + ";
1147         }
1148         else {
1149             code += Date.getFormatCode(ch);
1150         }
1151     }
1152     /** eval:var:zzzzzzzzzzzzz */
1153     eval(code.substring(0, code.length - 3) + ";}");
1154 };
1155
1156 // private
1157 Date.getFormatCode = function(character) {
1158     switch (character) {
1159     case "d":
1160         return "String.leftPad(this.getDate(), 2, '0') + ";
1161     case "D":
1162         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1163     case "j":
1164         return "this.getDate() + ";
1165     case "l":
1166         return "Date.dayNames[this.getDay()] + ";
1167     case "S":
1168         return "this.getSuffix() + ";
1169     case "w":
1170         return "this.getDay() + ";
1171     case "z":
1172         return "this.getDayOfYear() + ";
1173     case "W":
1174         return "this.getWeekOfYear() + ";
1175     case "F":
1176         return "Date.monthNames[this.getMonth()] + ";
1177     case "m":
1178         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1179     case "M":
1180         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1181     case "n":
1182         return "(this.getMonth() + 1) + ";
1183     case "t":
1184         return "this.getDaysInMonth() + ";
1185     case "L":
1186         return "(this.isLeapYear() ? 1 : 0) + ";
1187     case "Y":
1188         return "this.getFullYear() + ";
1189     case "y":
1190         return "('' + this.getFullYear()).substring(2, 4) + ";
1191     case "a":
1192         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1193     case "A":
1194         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1195     case "g":
1196         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1197     case "G":
1198         return "this.getHours() + ";
1199     case "h":
1200         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1201     case "H":
1202         return "String.leftPad(this.getHours(), 2, '0') + ";
1203     case "i":
1204         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1205     case "s":
1206         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1207     case "O":
1208         return "this.getGMTOffset() + ";
1209     case "P":
1210         return "this.getGMTColonOffset() + ";
1211     case "T":
1212         return "this.getTimezone() + ";
1213     case "Z":
1214         return "(this.getTimezoneOffset() * -60) + ";
1215     default:
1216         return "'" + String.escape(character) + "' + ";
1217     }
1218 };
1219
1220 /**
1221  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1222  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1223  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1224  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1225  * string or the parse operation will fail.
1226  * Example Usage:
1227 <pre><code>
1228 //dt = Fri May 25 2007 (current date)
1229 var dt = new Date();
1230
1231 //dt = Thu May 25 2006 (today's month/day in 2006)
1232 dt = Date.parseDate("2006", "Y");
1233
1234 //dt = Sun Jan 15 2006 (all date parts specified)
1235 dt = Date.parseDate("2006-1-15", "Y-m-d");
1236
1237 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1238 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1239 </code></pre>
1240  * @param {String} input The unparsed date as a string
1241  * @param {String} format The format the date is in
1242  * @return {Date} The parsed date
1243  * @static
1244  */
1245 Date.parseDate = function(input, format) {
1246     if (Date.parseFunctions[format] == null) {
1247         Date.createParser(format);
1248     }
1249     var func = Date.parseFunctions[format];
1250     return Date[func](input);
1251 };
1252 /**
1253  * @private
1254  */
1255
1256 Date.createParser = function(format) {
1257     var funcName = "parse" + Date.parseFunctions.count++;
1258     var regexNum = Date.parseRegexes.length;
1259     var currentGroup = 1;
1260     Date.parseFunctions[format] = funcName;
1261
1262     var code = "Date." + funcName + " = function(input){\n"
1263         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1264         + "var d = new Date();\n"
1265         + "y = d.getFullYear();\n"
1266         + "m = d.getMonth();\n"
1267         + "d = d.getDate();\n"
1268         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1269         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1270         + "if (results && results.length > 0) {";
1271     var regex = "";
1272
1273     var special = false;
1274     var ch = '';
1275     for (var i = 0; i < format.length; ++i) {
1276         ch = format.charAt(i);
1277         if (!special && ch == "\\") {
1278             special = true;
1279         }
1280         else if (special) {
1281             special = false;
1282             regex += String.escape(ch);
1283         }
1284         else {
1285             var obj = Date.formatCodeToRegex(ch, currentGroup);
1286             currentGroup += obj.g;
1287             regex += obj.s;
1288             if (obj.g && obj.c) {
1289                 code += obj.c;
1290             }
1291         }
1292     }
1293
1294     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1295         + "{v = new Date(y, m, d, h, i, s);}\n"
1296         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1297         + "{v = new Date(y, m, d, h, i);}\n"
1298         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1299         + "{v = new Date(y, m, d, h);}\n"
1300         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1301         + "{v = new Date(y, m, d);}\n"
1302         + "else if (y >= 0 && m >= 0)\n"
1303         + "{v = new Date(y, m);}\n"
1304         + "else if (y >= 0)\n"
1305         + "{v = new Date(y);}\n"
1306         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1307         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1308         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1309         + ";}";
1310
1311     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1312     /** eval:var:zzzzzzzzzzzzz */
1313     eval(code);
1314 };
1315
1316 // private
1317 Date.formatCodeToRegex = function(character, currentGroup) {
1318     switch (character) {
1319     case "D":
1320         return {g:0,
1321         c:null,
1322         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1323     case "j":
1324         return {g:1,
1325             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1326             s:"(\\d{1,2})"}; // day of month without leading zeroes
1327     case "d":
1328         return {g:1,
1329             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1330             s:"(\\d{2})"}; // day of month with leading zeroes
1331     case "l":
1332         return {g:0,
1333             c:null,
1334             s:"(?:" + Date.dayNames.join("|") + ")"};
1335     case "S":
1336         return {g:0,
1337             c:null,
1338             s:"(?:st|nd|rd|th)"};
1339     case "w":
1340         return {g:0,
1341             c:null,
1342             s:"\\d"};
1343     case "z":
1344         return {g:0,
1345             c:null,
1346             s:"(?:\\d{1,3})"};
1347     case "W":
1348         return {g:0,
1349             c:null,
1350             s:"(?:\\d{2})"};
1351     case "F":
1352         return {g:1,
1353             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1354             s:"(" + Date.monthNames.join("|") + ")"};
1355     case "M":
1356         return {g:1,
1357             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1358             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1359     case "n":
1360         return {g:1,
1361             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1362             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1363     case "m":
1364         return {g:1,
1365             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1366             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1367     case "t":
1368         return {g:0,
1369             c:null,
1370             s:"\\d{1,2}"};
1371     case "L":
1372         return {g:0,
1373             c:null,
1374             s:"(?:1|0)"};
1375     case "Y":
1376         return {g:1,
1377             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1378             s:"(\\d{4})"};
1379     case "y":
1380         return {g:1,
1381             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1382                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1383             s:"(\\d{1,2})"};
1384     case "a":
1385         return {g:1,
1386             c:"if (results[" + currentGroup + "] == 'am') {\n"
1387                 + "if (h == 12) { h = 0; }\n"
1388                 + "} else { if (h < 12) { h += 12; }}",
1389             s:"(am|pm)"};
1390     case "A":
1391         return {g:1,
1392             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1393                 + "if (h == 12) { h = 0; }\n"
1394                 + "} else { if (h < 12) { h += 12; }}",
1395             s:"(AM|PM)"};
1396     case "g":
1397     case "G":
1398         return {g:1,
1399             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1400             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1401     case "h":
1402     case "H":
1403         return {g:1,
1404             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1405             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1406     case "i":
1407         return {g:1,
1408             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1409             s:"(\\d{2})"};
1410     case "s":
1411         return {g:1,
1412             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1413             s:"(\\d{2})"};
1414     case "O":
1415         return {g:1,
1416             c:[
1417                 "o = results[", currentGroup, "];\n",
1418                 "var sn = o.substring(0,1);\n", // get + / - sign
1419                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1420                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1421                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1422                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1423             ].join(""),
1424             s:"([+\-]\\d{2,4})"};
1425     
1426     
1427     case "P":
1428         return {g:1,
1429                 c:[
1430                    "o = results[", currentGroup, "];\n",
1431                    "var sn = o.substring(0,1);\n",
1432                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1433                    "var mn = o.substring(4,6) % 60;\n",
1434                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1435                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1436             ].join(""),
1437             s:"([+\-]\\d{4})"};
1438     case "T":
1439         return {g:0,
1440             c:null,
1441             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1442     case "Z":
1443         return {g:1,
1444             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1445                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1446             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1447     default:
1448         return {g:0,
1449             c:null,
1450             s:String.escape(character)};
1451     }
1452 };
1453
1454 /**
1455  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1456  * @return {String} The abbreviated timezone name (e.g. 'CST')
1457  */
1458 Date.prototype.getTimezone = function() {
1459     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1460 };
1461
1462 /**
1463  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1464  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1465  */
1466 Date.prototype.getGMTOffset = function() {
1467     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1468         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1469         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1470 };
1471
1472 /**
1473  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1474  * @return {String} 2-characters representing hours and 2-characters representing minutes
1475  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1476  */
1477 Date.prototype.getGMTColonOffset = function() {
1478         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1479                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1480                 + ":"
1481                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1482 }
1483
1484 /**
1485  * Get the numeric day number of the year, adjusted for leap year.
1486  * @return {Number} 0 through 364 (365 in leap years)
1487  */
1488 Date.prototype.getDayOfYear = function() {
1489     var num = 0;
1490     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1491     for (var i = 0; i < this.getMonth(); ++i) {
1492         num += Date.daysInMonth[i];
1493     }
1494     return num + this.getDate() - 1;
1495 };
1496
1497 /**
1498  * Get the string representation of the numeric week number of the year
1499  * (equivalent to the format specifier 'W').
1500  * @return {String} '00' through '52'
1501  */
1502 Date.prototype.getWeekOfYear = function() {
1503     // Skip to Thursday of this week
1504     var now = this.getDayOfYear() + (4 - this.getDay());
1505     // Find the first Thursday of the year
1506     var jan1 = new Date(this.getFullYear(), 0, 1);
1507     var then = (7 - jan1.getDay() + 4);
1508     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1509 };
1510
1511 /**
1512  * Whether or not the current date is in a leap year.
1513  * @return {Boolean} True if the current date is in a leap year, else false
1514  */
1515 Date.prototype.isLeapYear = function() {
1516     var year = this.getFullYear();
1517     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1518 };
1519
1520 /**
1521  * Get the first day of the current month, adjusted for leap year.  The returned value
1522  * is the numeric day index within the week (0-6) which can be used in conjunction with
1523  * the {@link #monthNames} array to retrieve the textual day name.
1524  * Example:
1525  *<pre><code>
1526 var dt = new Date('1/10/2007');
1527 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1528 </code></pre>
1529  * @return {Number} The day number (0-6)
1530  */
1531 Date.prototype.getFirstDayOfMonth = function() {
1532     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1533     return (day < 0) ? (day + 7) : day;
1534 };
1535
1536 /**
1537  * Get the last day of the current month, adjusted for leap year.  The returned value
1538  * is the numeric day index within the week (0-6) which can be used in conjunction with
1539  * the {@link #monthNames} array to retrieve the textual day name.
1540  * Example:
1541  *<pre><code>
1542 var dt = new Date('1/10/2007');
1543 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1544 </code></pre>
1545  * @return {Number} The day number (0-6)
1546  */
1547 Date.prototype.getLastDayOfMonth = function() {
1548     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1549     return (day < 0) ? (day + 7) : day;
1550 };
1551
1552
1553 /**
1554  * Get the first date of this date's month
1555  * @return {Date}
1556  */
1557 Date.prototype.getFirstDateOfMonth = function() {
1558     return new Date(this.getFullYear(), this.getMonth(), 1);
1559 };
1560
1561 /**
1562  * Get the last date of this date's month
1563  * @return {Date}
1564  */
1565 Date.prototype.getLastDateOfMonth = function() {
1566     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1567 };
1568 /**
1569  * Get the number of days in the current month, adjusted for leap year.
1570  * @return {Number} The number of days in the month
1571  */
1572 Date.prototype.getDaysInMonth = function() {
1573     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1574     return Date.daysInMonth[this.getMonth()];
1575 };
1576
1577 /**
1578  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1579  * @return {String} 'st, 'nd', 'rd' or 'th'
1580  */
1581 Date.prototype.getSuffix = function() {
1582     switch (this.getDate()) {
1583         case 1:
1584         case 21:
1585         case 31:
1586             return "st";
1587         case 2:
1588         case 22:
1589             return "nd";
1590         case 3:
1591         case 23:
1592             return "rd";
1593         default:
1594             return "th";
1595     }
1596 };
1597
1598 // private
1599 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1600
1601 /**
1602  * An array of textual month names.
1603  * Override these values for international dates, for example...
1604  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1605  * @type Array
1606  * @static
1607  */
1608 Date.monthNames =
1609    ["January",
1610     "February",
1611     "March",
1612     "April",
1613     "May",
1614     "June",
1615     "July",
1616     "August",
1617     "September",
1618     "October",
1619     "November",
1620     "December"];
1621
1622 /**
1623  * An array of textual day names.
1624  * Override these values for international dates, for example...
1625  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1626  * @type Array
1627  * @static
1628  */
1629 Date.dayNames =
1630    ["Sunday",
1631     "Monday",
1632     "Tuesday",
1633     "Wednesday",
1634     "Thursday",
1635     "Friday",
1636     "Saturday"];
1637
1638 // private
1639 Date.y2kYear = 50;
1640 // private
1641 Date.monthNumbers = {
1642     Jan:0,
1643     Feb:1,
1644     Mar:2,
1645     Apr:3,
1646     May:4,
1647     Jun:5,
1648     Jul:6,
1649     Aug:7,
1650     Sep:8,
1651     Oct:9,
1652     Nov:10,
1653     Dec:11};
1654
1655 /**
1656  * Creates and returns a new Date instance with the exact same date value as the called instance.
1657  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1658  * variable will also be changed.  When the intention is to create a new variable that will not
1659  * modify the original instance, you should create a clone.
1660  *
1661  * Example of correctly cloning a date:
1662  * <pre><code>
1663 //wrong way:
1664 var orig = new Date('10/1/2006');
1665 var copy = orig;
1666 copy.setDate(5);
1667 document.write(orig);  //returns 'Thu Oct 05 2006'!
1668
1669 //correct way:
1670 var orig = new Date('10/1/2006');
1671 var copy = orig.clone();
1672 copy.setDate(5);
1673 document.write(orig);  //returns 'Thu Oct 01 2006'
1674 </code></pre>
1675  * @return {Date} The new Date instance
1676  */
1677 Date.prototype.clone = function() {
1678         return new Date(this.getTime());
1679 };
1680
1681 /**
1682  * Clears any time information from this date
1683  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1684  @return {Date} this or the clone
1685  */
1686 Date.prototype.clearTime = function(clone){
1687     if(clone){
1688         return this.clone().clearTime();
1689     }
1690     this.setHours(0);
1691     this.setMinutes(0);
1692     this.setSeconds(0);
1693     this.setMilliseconds(0);
1694     return this;
1695 };
1696
1697 // private
1698 // safari setMonth is broken -- check that this is only donw once...
1699 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1700     Date.brokenSetMonth = Date.prototype.setMonth;
1701         Date.prototype.setMonth = function(num){
1702                 if(num <= -1){
1703                         var n = Math.ceil(-num);
1704                         var back_year = Math.ceil(n/12);
1705                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1706                         this.setFullYear(this.getFullYear() - back_year);
1707                         return Date.brokenSetMonth.call(this, month);
1708                 } else {
1709                         return Date.brokenSetMonth.apply(this, arguments);
1710                 }
1711         };
1712 }
1713
1714 /** Date interval constant 
1715 * @static 
1716 * @type String */
1717 Date.MILLI = "ms";
1718 /** Date interval constant 
1719 * @static 
1720 * @type String */
1721 Date.SECOND = "s";
1722 /** Date interval constant 
1723 * @static 
1724 * @type String */
1725 Date.MINUTE = "mi";
1726 /** Date interval constant 
1727 * @static 
1728 * @type String */
1729 Date.HOUR = "h";
1730 /** Date interval constant 
1731 * @static 
1732 * @type String */
1733 Date.DAY = "d";
1734 /** Date interval constant 
1735 * @static 
1736 * @type String */
1737 Date.MONTH = "mo";
1738 /** Date interval constant 
1739 * @static 
1740 * @type String */
1741 Date.YEAR = "y";
1742
1743 /**
1744  * Provides a convenient method of performing basic date arithmetic.  This method
1745  * does not modify the Date instance being called - it creates and returns
1746  * a new Date instance containing the resulting date value.
1747  *
1748  * Examples:
1749  * <pre><code>
1750 //Basic usage:
1751 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1752 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1753
1754 //Negative values will subtract correctly:
1755 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1756 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1757
1758 //You can even chain several calls together in one line!
1759 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1760 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1761  </code></pre>
1762  *
1763  * @param {String} interval   A valid date interval enum value
1764  * @param {Number} value      The amount to add to the current date
1765  * @return {Date} The new Date instance
1766  */
1767 Date.prototype.add = function(interval, value){
1768   var d = this.clone();
1769   if (!interval || value === 0) { return d; }
1770   switch(interval.toLowerCase()){
1771     case Date.MILLI:
1772       d.setMilliseconds(this.getMilliseconds() + value);
1773       break;
1774     case Date.SECOND:
1775       d.setSeconds(this.getSeconds() + value);
1776       break;
1777     case Date.MINUTE:
1778       d.setMinutes(this.getMinutes() + value);
1779       break;
1780     case Date.HOUR:
1781       d.setHours(this.getHours() + value);
1782       break;
1783     case Date.DAY:
1784       d.setDate(this.getDate() + value);
1785       break;
1786     case Date.MONTH:
1787       var day = this.getDate();
1788       if(day > 28){
1789           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1790       }
1791       d.setDate(day);
1792       d.setMonth(this.getMonth() + value);
1793       break;
1794     case Date.YEAR:
1795       d.setFullYear(this.getFullYear() + value);
1796       break;
1797   }
1798   return d;
1799 };
1800 /*
1801  * Based on:
1802  * Ext JS Library 1.1.1
1803  * Copyright(c) 2006-2007, Ext JS, LLC.
1804  *
1805  * Originally Released Under LGPL - original licence link has changed is not relivant.
1806  *
1807  * Fork - LGPL
1808  * <script type="text/javascript">
1809  */
1810
1811 /**
1812  * @class Roo.lib.Dom
1813  * @static
1814  * 
1815  * Dom utils (from YIU afaik)
1816  * 
1817  **/
1818 Roo.lib.Dom = {
1819     /**
1820      * Get the view width
1821      * @param {Boolean} full True will get the full document, otherwise it's the view width
1822      * @return {Number} The width
1823      */
1824      
1825     getViewWidth : function(full) {
1826         return full ? this.getDocumentWidth() : this.getViewportWidth();
1827     },
1828     /**
1829      * Get the view height
1830      * @param {Boolean} full True will get the full document, otherwise it's the view height
1831      * @return {Number} The height
1832      */
1833     getViewHeight : function(full) {
1834         return full ? this.getDocumentHeight() : this.getViewportHeight();
1835     },
1836
1837     getDocumentHeight: function() {
1838         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1839         return Math.max(scrollHeight, this.getViewportHeight());
1840     },
1841
1842     getDocumentWidth: function() {
1843         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1844         return Math.max(scrollWidth, this.getViewportWidth());
1845     },
1846
1847     getViewportHeight: function() {
1848         var height = self.innerHeight;
1849         var mode = document.compatMode;
1850
1851         if ((mode || Roo.isIE) && !Roo.isOpera) {
1852             height = (mode == "CSS1Compat") ?
1853                      document.documentElement.clientHeight :
1854                      document.body.clientHeight;
1855         }
1856
1857         return height;
1858     },
1859
1860     getViewportWidth: function() {
1861         var width = self.innerWidth;
1862         var mode = document.compatMode;
1863
1864         if (mode || Roo.isIE) {
1865             width = (mode == "CSS1Compat") ?
1866                     document.documentElement.clientWidth :
1867                     document.body.clientWidth;
1868         }
1869         return width;
1870     },
1871
1872     isAncestor : function(p, c) {
1873         p = Roo.getDom(p);
1874         c = Roo.getDom(c);
1875         if (!p || !c) {
1876             return false;
1877         }
1878
1879         if (p.contains && !Roo.isSafari) {
1880             return p.contains(c);
1881         } else if (p.compareDocumentPosition) {
1882             return !!(p.compareDocumentPosition(c) & 16);
1883         } else {
1884             var parent = c.parentNode;
1885             while (parent) {
1886                 if (parent == p) {
1887                     return true;
1888                 }
1889                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1890                     return false;
1891                 }
1892                 parent = parent.parentNode;
1893             }
1894             return false;
1895         }
1896     },
1897
1898     getRegion : function(el) {
1899         return Roo.lib.Region.getRegion(el);
1900     },
1901
1902     getY : function(el) {
1903         return this.getXY(el)[1];
1904     },
1905
1906     getX : function(el) {
1907         return this.getXY(el)[0];
1908     },
1909
1910     getXY : function(el) {
1911         var p, pe, b, scroll, bd = document.body;
1912         el = Roo.getDom(el);
1913         var fly = Roo.lib.AnimBase.fly;
1914         if (el.getBoundingClientRect) {
1915             b = el.getBoundingClientRect();
1916             scroll = fly(document).getScroll();
1917             return [b.left + scroll.left, b.top + scroll.top];
1918         }
1919         var x = 0, y = 0;
1920
1921         p = el;
1922
1923         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1924
1925         while (p) {
1926
1927             x += p.offsetLeft;
1928             y += p.offsetTop;
1929
1930             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1931                 hasAbsolute = true;
1932             }
1933
1934             if (Roo.isGecko) {
1935                 pe = fly(p);
1936
1937                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1938                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1939
1940
1941                 x += bl;
1942                 y += bt;
1943
1944
1945                 if (p != el && pe.getStyle('overflow') != 'visible') {
1946                     x += bl;
1947                     y += bt;
1948                 }
1949             }
1950             p = p.offsetParent;
1951         }
1952
1953         if (Roo.isSafari && hasAbsolute) {
1954             x -= bd.offsetLeft;
1955             y -= bd.offsetTop;
1956         }
1957
1958         if (Roo.isGecko && !hasAbsolute) {
1959             var dbd = fly(bd);
1960             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1961             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1962         }
1963
1964         p = el.parentNode;
1965         while (p && p != bd) {
1966             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1967                 x -= p.scrollLeft;
1968                 y -= p.scrollTop;
1969             }
1970             p = p.parentNode;
1971         }
1972         return [x, y];
1973     },
1974  
1975   
1976
1977
1978     setXY : function(el, xy) {
1979         el = Roo.fly(el, '_setXY');
1980         el.position();
1981         var pts = el.translatePoints(xy);
1982         if (xy[0] !== false) {
1983             el.dom.style.left = pts.left + "px";
1984         }
1985         if (xy[1] !== false) {
1986             el.dom.style.top = pts.top + "px";
1987         }
1988     },
1989
1990     setX : function(el, x) {
1991         this.setXY(el, [x, false]);
1992     },
1993
1994     setY : function(el, y) {
1995         this.setXY(el, [false, y]);
1996     }
1997 };
1998 /*
1999  * Portions of this file are based on pieces of Yahoo User Interface Library
2000  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2001  * YUI licensed under the BSD License:
2002  * http://developer.yahoo.net/yui/license.txt
2003  * <script type="text/javascript">
2004  *
2005  */
2006
2007 Roo.lib.Event = function() {
2008     var loadComplete = false;
2009     var listeners = [];
2010     var unloadListeners = [];
2011     var retryCount = 0;
2012     var onAvailStack = [];
2013     var counter = 0;
2014     var lastError = null;
2015
2016     return {
2017         POLL_RETRYS: 200,
2018         POLL_INTERVAL: 20,
2019         EL: 0,
2020         TYPE: 1,
2021         FN: 2,
2022         WFN: 3,
2023         OBJ: 3,
2024         ADJ_SCOPE: 4,
2025         _interval: null,
2026
2027         startInterval: function() {
2028             if (!this._interval) {
2029                 var self = this;
2030                 var callback = function() {
2031                     self._tryPreloadAttach();
2032                 };
2033                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2034
2035             }
2036         },
2037
2038         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2039             onAvailStack.push({ id:         p_id,
2040                 fn:         p_fn,
2041                 obj:        p_obj,
2042                 override:   p_override,
2043                 checkReady: false    });
2044
2045             retryCount = this.POLL_RETRYS;
2046             this.startInterval();
2047         },
2048
2049
2050         addListener: function(el, eventName, fn) {
2051             el = Roo.getDom(el);
2052             if (!el || !fn) {
2053                 return false;
2054             }
2055
2056             if ("unload" == eventName) {
2057                 unloadListeners[unloadListeners.length] =
2058                 [el, eventName, fn];
2059                 return true;
2060             }
2061
2062             var wrappedFn = function(e) {
2063                 return fn(Roo.lib.Event.getEvent(e));
2064             };
2065
2066             var li = [el, eventName, fn, wrappedFn];
2067
2068             var index = listeners.length;
2069             listeners[index] = li;
2070
2071             this.doAdd(el, eventName, wrappedFn, false);
2072             return true;
2073
2074         },
2075
2076
2077         removeListener: function(el, eventName, fn) {
2078             var i, len;
2079
2080             el = Roo.getDom(el);
2081
2082             if(!fn) {
2083                 return this.purgeElement(el, false, eventName);
2084             }
2085
2086
2087             if ("unload" == eventName) {
2088
2089                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2090                     var li = unloadListeners[i];
2091                     if (li &&
2092                         li[0] == el &&
2093                         li[1] == eventName &&
2094                         li[2] == fn) {
2095                         unloadListeners.splice(i, 1);
2096                         return true;
2097                     }
2098                 }
2099
2100                 return false;
2101             }
2102
2103             var cacheItem = null;
2104
2105
2106             var index = arguments[3];
2107
2108             if ("undefined" == typeof index) {
2109                 index = this._getCacheIndex(el, eventName, fn);
2110             }
2111
2112             if (index >= 0) {
2113                 cacheItem = listeners[index];
2114             }
2115
2116             if (!el || !cacheItem) {
2117                 return false;
2118             }
2119
2120             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2121
2122             delete listeners[index][this.WFN];
2123             delete listeners[index][this.FN];
2124             listeners.splice(index, 1);
2125
2126             return true;
2127
2128         },
2129
2130
2131         getTarget: function(ev, resolveTextNode) {
2132             ev = ev.browserEvent || ev;
2133             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2134             var t = ev.target || ev.srcElement;
2135             return this.resolveTextNode(t);
2136         },
2137
2138
2139         resolveTextNode: function(node) {
2140             if (Roo.isSafari && node && 3 == node.nodeType) {
2141                 return node.parentNode;
2142             } else {
2143                 return node;
2144             }
2145         },
2146
2147
2148         getPageX: function(ev) {
2149             ev = ev.browserEvent || ev;
2150             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2151             var x = ev.pageX;
2152             if (!x && 0 !== x) {
2153                 x = ev.clientX || 0;
2154
2155                 if (Roo.isIE) {
2156                     x += this.getScroll()[1];
2157                 }
2158             }
2159
2160             return x;
2161         },
2162
2163
2164         getPageY: function(ev) {
2165             ev = ev.browserEvent || ev;
2166             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2167             var y = ev.pageY;
2168             if (!y && 0 !== y) {
2169                 y = ev.clientY || 0;
2170
2171                 if (Roo.isIE) {
2172                     y += this.getScroll()[0];
2173                 }
2174             }
2175
2176
2177             return y;
2178         },
2179
2180
2181         getXY: function(ev) {
2182             ev = ev.browserEvent || ev;
2183             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2184             return [this.getPageX(ev), this.getPageY(ev)];
2185         },
2186
2187
2188         getRelatedTarget: function(ev) {
2189             ev = ev.browserEvent || ev;
2190             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2191             var t = ev.relatedTarget;
2192             if (!t) {
2193                 if (ev.type == "mouseout") {
2194                     t = ev.toElement;
2195                 } else if (ev.type == "mouseover") {
2196                     t = ev.fromElement;
2197                 }
2198             }
2199
2200             return this.resolveTextNode(t);
2201         },
2202
2203
2204         getTime: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2207             if (!ev.time) {
2208                 var t = new Date().getTime();
2209                 try {
2210                     ev.time = t;
2211                 } catch(ex) {
2212                     this.lastError = ex;
2213                     return t;
2214                 }
2215             }
2216
2217             return ev.time;
2218         },
2219
2220
2221         stopEvent: function(ev) {
2222             this.stopPropagation(ev);
2223             this.preventDefault(ev);
2224         },
2225
2226
2227         stopPropagation: function(ev) {
2228             ev = ev.browserEvent || ev;
2229             if (ev.stopPropagation) {
2230                 ev.stopPropagation();
2231             } else {
2232                 ev.cancelBubble = true;
2233             }
2234         },
2235
2236
2237         preventDefault: function(ev) {
2238             ev = ev.browserEvent || ev;
2239             if(ev.preventDefault) {
2240                 ev.preventDefault();
2241             } else {
2242                 ev.returnValue = false;
2243             }
2244         },
2245
2246
2247         getEvent: function(e) {
2248             var ev = e || window.event;
2249             if (!ev) {
2250                 var c = this.getEvent.caller;
2251                 while (c) {
2252                     ev = c.arguments[0];
2253                     if (ev && Event == ev.constructor) {
2254                         break;
2255                     }
2256                     c = c.caller;
2257                 }
2258             }
2259             return ev;
2260         },
2261
2262
2263         getCharCode: function(ev) {
2264             ev = ev.browserEvent || ev;
2265             return ev.charCode || ev.keyCode || 0;
2266         },
2267
2268
2269         _getCacheIndex: function(el, eventName, fn) {
2270             for (var i = 0,len = listeners.length; i < len; ++i) {
2271                 var li = listeners[i];
2272                 if (li &&
2273                     li[this.FN] == fn &&
2274                     li[this.EL] == el &&
2275                     li[this.TYPE] == eventName) {
2276                     return i;
2277                 }
2278             }
2279
2280             return -1;
2281         },
2282
2283
2284         elCache: {},
2285
2286
2287         getEl: function(id) {
2288             return document.getElementById(id);
2289         },
2290
2291
2292         clearCache: function() {
2293         },
2294
2295
2296         _load: function(e) {
2297             loadComplete = true;
2298             var EU = Roo.lib.Event;
2299
2300
2301             if (Roo.isIE) {
2302                 EU.doRemove(window, "load", EU._load);
2303             }
2304         },
2305
2306
2307         _tryPreloadAttach: function() {
2308
2309             if (this.locked) {
2310                 return false;
2311             }
2312
2313             this.locked = true;
2314
2315
2316             var tryAgain = !loadComplete;
2317             if (!tryAgain) {
2318                 tryAgain = (retryCount > 0);
2319             }
2320
2321
2322             var notAvail = [];
2323             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2324                 var item = onAvailStack[i];
2325                 if (item) {
2326                     var el = this.getEl(item.id);
2327
2328                     if (el) {
2329                         if (!item.checkReady ||
2330                             loadComplete ||
2331                             el.nextSibling ||
2332                             (document && document.body)) {
2333
2334                             var scope = el;
2335                             if (item.override) {
2336                                 if (item.override === true) {
2337                                     scope = item.obj;
2338                                 } else {
2339                                     scope = item.override;
2340                                 }
2341                             }
2342                             item.fn.call(scope, item.obj);
2343                             onAvailStack[i] = null;
2344                         }
2345                     } else {
2346                         notAvail.push(item);
2347                     }
2348                 }
2349             }
2350
2351             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2352
2353             if (tryAgain) {
2354
2355                 this.startInterval();
2356             } else {
2357                 clearInterval(this._interval);
2358                 this._interval = null;
2359             }
2360
2361             this.locked = false;
2362
2363             return true;
2364
2365         },
2366
2367
2368         purgeElement: function(el, recurse, eventName) {
2369             var elListeners = this.getListeners(el, eventName);
2370             if (elListeners) {
2371                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2372                     var l = elListeners[i];
2373                     this.removeListener(el, l.type, l.fn);
2374                 }
2375             }
2376
2377             if (recurse && el && el.childNodes) {
2378                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2379                     this.purgeElement(el.childNodes[i], recurse, eventName);
2380                 }
2381             }
2382         },
2383
2384
2385         getListeners: function(el, eventName) {
2386             var results = [], searchLists;
2387             if (!eventName) {
2388                 searchLists = [listeners, unloadListeners];
2389             } else if (eventName == "unload") {
2390                 searchLists = [unloadListeners];
2391             } else {
2392                 searchLists = [listeners];
2393             }
2394
2395             for (var j = 0; j < searchLists.length; ++j) {
2396                 var searchList = searchLists[j];
2397                 if (searchList && searchList.length > 0) {
2398                     for (var i = 0,len = searchList.length; i < len; ++i) {
2399                         var l = searchList[i];
2400                         if (l && l[this.EL] === el &&
2401                             (!eventName || eventName === l[this.TYPE])) {
2402                             results.push({
2403                                 type:   l[this.TYPE],
2404                                 fn:     l[this.FN],
2405                                 obj:    l[this.OBJ],
2406                                 adjust: l[this.ADJ_SCOPE],
2407                                 index:  i
2408                             });
2409                         }
2410                     }
2411                 }
2412             }
2413
2414             return (results.length) ? results : null;
2415         },
2416
2417
2418         _unload: function(e) {
2419
2420             var EU = Roo.lib.Event, i, j, l, len, index;
2421
2422             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2423                 l = unloadListeners[i];
2424                 if (l) {
2425                     var scope = window;
2426                     if (l[EU.ADJ_SCOPE]) {
2427                         if (l[EU.ADJ_SCOPE] === true) {
2428                             scope = l[EU.OBJ];
2429                         } else {
2430                             scope = l[EU.ADJ_SCOPE];
2431                         }
2432                     }
2433                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2434                     unloadListeners[i] = null;
2435                     l = null;
2436                     scope = null;
2437                 }
2438             }
2439
2440             unloadListeners = null;
2441
2442             if (listeners && listeners.length > 0) {
2443                 j = listeners.length;
2444                 while (j) {
2445                     index = j - 1;
2446                     l = listeners[index];
2447                     if (l) {
2448                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2449                                 l[EU.FN], index);
2450                     }
2451                     j = j - 1;
2452                 }
2453                 l = null;
2454
2455                 EU.clearCache();
2456             }
2457
2458             EU.doRemove(window, "unload", EU._unload);
2459
2460         },
2461
2462
2463         getScroll: function() {
2464             var dd = document.documentElement, db = document.body;
2465             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2466                 return [dd.scrollTop, dd.scrollLeft];
2467             } else if (db) {
2468                 return [db.scrollTop, db.scrollLeft];
2469             } else {
2470                 return [0, 0];
2471             }
2472         },
2473
2474
2475         doAdd: function () {
2476             if (window.addEventListener) {
2477                 return function(el, eventName, fn, capture) {
2478                     el.addEventListener(eventName, fn, (capture));
2479                 };
2480             } else if (window.attachEvent) {
2481                 return function(el, eventName, fn, capture) {
2482                     el.attachEvent("on" + eventName, fn);
2483                 };
2484             } else {
2485                 return function() {
2486                 };
2487             }
2488         }(),
2489
2490
2491         doRemove: function() {
2492             if (window.removeEventListener) {
2493                 return function (el, eventName, fn, capture) {
2494                     el.removeEventListener(eventName, fn, (capture));
2495                 };
2496             } else if (window.detachEvent) {
2497                 return function (el, eventName, fn) {
2498                     el.detachEvent("on" + eventName, fn);
2499                 };
2500             } else {
2501                 return function() {
2502                 };
2503             }
2504         }()
2505     };
2506     
2507 }();
2508 (function() {     
2509    
2510     var E = Roo.lib.Event;
2511     E.on = E.addListener;
2512     E.un = E.removeListener;
2513
2514     if (document && document.body) {
2515         E._load();
2516     } else {
2517         E.doAdd(window, "load", E._load);
2518     }
2519     E.doAdd(window, "unload", E._unload);
2520     E._tryPreloadAttach();
2521 })();
2522
2523 /*
2524  * Portions of this file are based on pieces of Yahoo User Interface Library
2525  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2526  * YUI licensed under the BSD License:
2527  * http://developer.yahoo.net/yui/license.txt
2528  * <script type="text/javascript">
2529  *
2530  */
2531
2532 (function() {
2533     /**
2534      * @class Roo.lib.Ajax
2535      *
2536      */
2537     Roo.lib.Ajax = {
2538         /**
2539          * @static 
2540          */
2541         request : function(method, uri, cb, data, options) {
2542             if(options){
2543                 var hs = options.headers;
2544                 if(hs){
2545                     for(var h in hs){
2546                         if(hs.hasOwnProperty(h)){
2547                             this.initHeader(h, hs[h], false);
2548                         }
2549                     }
2550                 }
2551                 if(options.xmlData){
2552                     this.initHeader('Content-Type', 'text/xml', false);
2553                     method = 'POST';
2554                     data = options.xmlData;
2555                 }
2556             }
2557
2558             return this.asyncRequest(method, uri, cb, data);
2559         },
2560
2561         serializeForm : function(form) {
2562             if(typeof form == 'string') {
2563                 form = (document.getElementById(form) || document.forms[form]);
2564             }
2565
2566             var el, name, val, disabled, data = '', hasSubmit = false;
2567             for (var i = 0; i < form.elements.length; i++) {
2568                 el = form.elements[i];
2569                 disabled = form.elements[i].disabled;
2570                 name = form.elements[i].name;
2571                 val = form.elements[i].value;
2572
2573                 if (!disabled && name){
2574                     switch (el.type)
2575                             {
2576                         case 'select-one':
2577                         case 'select-multiple':
2578                             for (var j = 0; j < el.options.length; j++) {
2579                                 if (el.options[j].selected) {
2580                                     if (Roo.isIE) {
2581                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2582                                     }
2583                                     else {
2584                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2585                                     }
2586                                 }
2587                             }
2588                             break;
2589                         case 'radio':
2590                         case 'checkbox':
2591                             if (el.checked) {
2592                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2593                             }
2594                             break;
2595                         case 'file':
2596
2597                         case undefined:
2598
2599                         case 'reset':
2600
2601                         case 'button':
2602
2603                             break;
2604                         case 'submit':
2605                             if(hasSubmit == false) {
2606                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2607                                 hasSubmit = true;
2608                             }
2609                             break;
2610                         default:
2611                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2612                             break;
2613                     }
2614                 }
2615             }
2616             data = data.substr(0, data.length - 1);
2617             return data;
2618         },
2619
2620         headers:{},
2621
2622         hasHeaders:false,
2623
2624         useDefaultHeader:true,
2625
2626         defaultPostHeader:'application/x-www-form-urlencoded',
2627
2628         useDefaultXhrHeader:true,
2629
2630         defaultXhrHeader:'XMLHttpRequest',
2631
2632         hasDefaultHeaders:true,
2633
2634         defaultHeaders:{},
2635
2636         poll:{},
2637
2638         timeout:{},
2639
2640         pollInterval:50,
2641
2642         transactionId:0,
2643
2644         setProgId:function(id)
2645         {
2646             this.activeX.unshift(id);
2647         },
2648
2649         setDefaultPostHeader:function(b)
2650         {
2651             this.useDefaultHeader = b;
2652         },
2653
2654         setDefaultXhrHeader:function(b)
2655         {
2656             this.useDefaultXhrHeader = b;
2657         },
2658
2659         setPollingInterval:function(i)
2660         {
2661             if (typeof i == 'number' && isFinite(i)) {
2662                 this.pollInterval = i;
2663             }
2664         },
2665
2666         createXhrObject:function(transactionId)
2667         {
2668             var obj,http;
2669             try
2670             {
2671
2672                 http = new XMLHttpRequest();
2673
2674                 obj = { conn:http, tId:transactionId };
2675             }
2676             catch(e)
2677             {
2678                 for (var i = 0; i < this.activeX.length; ++i) {
2679                     try
2680                     {
2681
2682                         http = new ActiveXObject(this.activeX[i]);
2683
2684                         obj = { conn:http, tId:transactionId };
2685                         break;
2686                     }
2687                     catch(e) {
2688                     }
2689                 }
2690             }
2691             finally
2692             {
2693                 return obj;
2694             }
2695         },
2696
2697         getConnectionObject:function()
2698         {
2699             var o;
2700             var tId = this.transactionId;
2701
2702             try
2703             {
2704                 o = this.createXhrObject(tId);
2705                 if (o) {
2706                     this.transactionId++;
2707                 }
2708             }
2709             catch(e) {
2710             }
2711             finally
2712             {
2713                 return o;
2714             }
2715         },
2716
2717         asyncRequest:function(method, uri, callback, postData)
2718         {
2719             var o = this.getConnectionObject();
2720
2721             if (!o) {
2722                 return null;
2723             }
2724             else {
2725                 o.conn.open(method, uri, true);
2726
2727                 if (this.useDefaultXhrHeader) {
2728                     if (!this.defaultHeaders['X-Requested-With']) {
2729                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2730                     }
2731                 }
2732
2733                 if(postData && this.useDefaultHeader){
2734                     this.initHeader('Content-Type', this.defaultPostHeader);
2735                 }
2736
2737                  if (this.hasDefaultHeaders || this.hasHeaders) {
2738                     this.setHeader(o);
2739                 }
2740
2741                 this.handleReadyState(o, callback);
2742                 o.conn.send(postData || null);
2743
2744                 return o;
2745             }
2746         },
2747
2748         handleReadyState:function(o, callback)
2749         {
2750             var oConn = this;
2751
2752             if (callback && callback.timeout) {
2753                 
2754                 this.timeout[o.tId] = window.setTimeout(function() {
2755                     oConn.abort(o, callback, true);
2756                 }, callback.timeout);
2757             }
2758
2759             this.poll[o.tId] = window.setInterval(
2760                     function() {
2761                         if (o.conn && o.conn.readyState == 4) {
2762                             window.clearInterval(oConn.poll[o.tId]);
2763                             delete oConn.poll[o.tId];
2764
2765                             if(callback && callback.timeout) {
2766                                 window.clearTimeout(oConn.timeout[o.tId]);
2767                                 delete oConn.timeout[o.tId];
2768                             }
2769
2770                             oConn.handleTransactionResponse(o, callback);
2771                         }
2772                     }
2773                     , this.pollInterval);
2774         },
2775
2776         handleTransactionResponse:function(o, callback, isAbort)
2777         {
2778
2779             if (!callback) {
2780                 this.releaseObject(o);
2781                 return;
2782             }
2783
2784             var httpStatus, responseObject;
2785
2786             try
2787             {
2788                 if (o.conn.status !== undefined && o.conn.status != 0) {
2789                     httpStatus = o.conn.status;
2790                 }
2791                 else {
2792                     httpStatus = 13030;
2793                 }
2794             }
2795             catch(e) {
2796
2797
2798                 httpStatus = 13030;
2799             }
2800
2801             if (httpStatus >= 200 && httpStatus < 300) {
2802                 responseObject = this.createResponseObject(o, callback.argument);
2803                 if (callback.success) {
2804                     if (!callback.scope) {
2805                         callback.success(responseObject);
2806                     }
2807                     else {
2808
2809
2810                         callback.success.apply(callback.scope, [responseObject]);
2811                     }
2812                 }
2813             }
2814             else {
2815                 switch (httpStatus) {
2816
2817                     case 12002:
2818                     case 12029:
2819                     case 12030:
2820                     case 12031:
2821                     case 12152:
2822                     case 13030:
2823                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2824                         if (callback.failure) {
2825                             if (!callback.scope) {
2826                                 callback.failure(responseObject);
2827                             }
2828                             else {
2829                                 callback.failure.apply(callback.scope, [responseObject]);
2830                             }
2831                         }
2832                         break;
2833                     default:
2834                         responseObject = this.createResponseObject(o, callback.argument);
2835                         if (callback.failure) {
2836                             if (!callback.scope) {
2837                                 callback.failure(responseObject);
2838                             }
2839                             else {
2840                                 callback.failure.apply(callback.scope, [responseObject]);
2841                             }
2842                         }
2843                 }
2844             }
2845
2846             this.releaseObject(o);
2847             responseObject = null;
2848         },
2849
2850         createResponseObject:function(o, callbackArg)
2851         {
2852             var obj = {};
2853             var headerObj = {};
2854
2855             try
2856             {
2857                 var headerStr = o.conn.getAllResponseHeaders();
2858                 var header = headerStr.split('\n');
2859                 for (var i = 0; i < header.length; i++) {
2860                     var delimitPos = header[i].indexOf(':');
2861                     if (delimitPos != -1) {
2862                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2863                     }
2864                 }
2865             }
2866             catch(e) {
2867             }
2868
2869             obj.tId = o.tId;
2870             obj.status = o.conn.status;
2871             obj.statusText = o.conn.statusText;
2872             obj.getResponseHeader = headerObj;
2873             obj.getAllResponseHeaders = headerStr;
2874             obj.responseText = o.conn.responseText;
2875             obj.responseXML = o.conn.responseXML;
2876
2877             if (typeof callbackArg !== undefined) {
2878                 obj.argument = callbackArg;
2879             }
2880
2881             return obj;
2882         },
2883
2884         createExceptionObject:function(tId, callbackArg, isAbort)
2885         {
2886             var COMM_CODE = 0;
2887             var COMM_ERROR = 'communication failure';
2888             var ABORT_CODE = -1;
2889             var ABORT_ERROR = 'transaction aborted';
2890
2891             var obj = {};
2892
2893             obj.tId = tId;
2894             if (isAbort) {
2895                 obj.status = ABORT_CODE;
2896                 obj.statusText = ABORT_ERROR;
2897             }
2898             else {
2899                 obj.status = COMM_CODE;
2900                 obj.statusText = COMM_ERROR;
2901             }
2902
2903             if (callbackArg) {
2904                 obj.argument = callbackArg;
2905             }
2906
2907             return obj;
2908         },
2909
2910         initHeader:function(label, value, isDefault)
2911         {
2912             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2913
2914             if (headerObj[label] === undefined) {
2915                 headerObj[label] = value;
2916             }
2917             else {
2918
2919
2920                 headerObj[label] = value + "," + headerObj[label];
2921             }
2922
2923             if (isDefault) {
2924                 this.hasDefaultHeaders = true;
2925             }
2926             else {
2927                 this.hasHeaders = true;
2928             }
2929         },
2930
2931
2932         setHeader:function(o)
2933         {
2934             if (this.hasDefaultHeaders) {
2935                 for (var prop in this.defaultHeaders) {
2936                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2937                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2938                     }
2939                 }
2940             }
2941
2942             if (this.hasHeaders) {
2943                 for (var prop in this.headers) {
2944                     if (this.headers.hasOwnProperty(prop)) {
2945                         o.conn.setRequestHeader(prop, this.headers[prop]);
2946                     }
2947                 }
2948                 this.headers = {};
2949                 this.hasHeaders = false;
2950             }
2951         },
2952
2953         resetDefaultHeaders:function() {
2954             delete this.defaultHeaders;
2955             this.defaultHeaders = {};
2956             this.hasDefaultHeaders = false;
2957         },
2958
2959         abort:function(o, callback, isTimeout)
2960         {
2961             if(this.isCallInProgress(o)) {
2962                 o.conn.abort();
2963                 window.clearInterval(this.poll[o.tId]);
2964                 delete this.poll[o.tId];
2965                 if (isTimeout) {
2966                     delete this.timeout[o.tId];
2967                 }
2968
2969                 this.handleTransactionResponse(o, callback, true);
2970
2971                 return true;
2972             }
2973             else {
2974                 return false;
2975             }
2976         },
2977
2978
2979         isCallInProgress:function(o)
2980         {
2981             if (o && o.conn) {
2982                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2983             }
2984             else {
2985
2986                 return false;
2987             }
2988         },
2989
2990
2991         releaseObject:function(o)
2992         {
2993
2994             o.conn = null;
2995
2996             o = null;
2997         },
2998
2999         activeX:[
3000         'MSXML2.XMLHTTP.3.0',
3001         'MSXML2.XMLHTTP',
3002         'Microsoft.XMLHTTP'
3003         ]
3004
3005
3006     };
3007 })();/*
3008  * Portions of this file are based on pieces of Yahoo User Interface Library
3009  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3010  * YUI licensed under the BSD License:
3011  * http://developer.yahoo.net/yui/license.txt
3012  * <script type="text/javascript">
3013  *
3014  */
3015
3016 Roo.lib.Region = function(t, r, b, l) {
3017     this.top = t;
3018     this[1] = t;
3019     this.right = r;
3020     this.bottom = b;
3021     this.left = l;
3022     this[0] = l;
3023 };
3024
3025
3026 Roo.lib.Region.prototype = {
3027     contains : function(region) {
3028         return ( region.left >= this.left &&
3029                  region.right <= this.right &&
3030                  region.top >= this.top &&
3031                  region.bottom <= this.bottom    );
3032
3033     },
3034
3035     getArea : function() {
3036         return ( (this.bottom - this.top) * (this.right - this.left) );
3037     },
3038
3039     intersect : function(region) {
3040         var t = Math.max(this.top, region.top);
3041         var r = Math.min(this.right, region.right);
3042         var b = Math.min(this.bottom, region.bottom);
3043         var l = Math.max(this.left, region.left);
3044
3045         if (b >= t && r >= l) {
3046             return new Roo.lib.Region(t, r, b, l);
3047         } else {
3048             return null;
3049         }
3050     },
3051     union : function(region) {
3052         var t = Math.min(this.top, region.top);
3053         var r = Math.max(this.right, region.right);
3054         var b = Math.max(this.bottom, region.bottom);
3055         var l = Math.min(this.left, region.left);
3056
3057         return new Roo.lib.Region(t, r, b, l);
3058     },
3059
3060     adjust : function(t, l, b, r) {
3061         this.top += t;
3062         this.left += l;
3063         this.right += r;
3064         this.bottom += b;
3065         return this;
3066     }
3067 };
3068
3069 Roo.lib.Region.getRegion = function(el) {
3070     var p = Roo.lib.Dom.getXY(el);
3071
3072     var t = p[1];
3073     var r = p[0] + el.offsetWidth;
3074     var b = p[1] + el.offsetHeight;
3075     var l = p[0];
3076
3077     return new Roo.lib.Region(t, r, b, l);
3078 };
3079 /*
3080  * Portions of this file are based on pieces of Yahoo User Interface Library
3081  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3082  * YUI licensed under the BSD License:
3083  * http://developer.yahoo.net/yui/license.txt
3084  * <script type="text/javascript">
3085  *
3086  */
3087 //@@dep Roo.lib.Region
3088
3089
3090 Roo.lib.Point = function(x, y) {
3091     if (x instanceof Array) {
3092         y = x[1];
3093         x = x[0];
3094     }
3095     this.x = this.right = this.left = this[0] = x;
3096     this.y = this.top = this.bottom = this[1] = y;
3097 };
3098
3099 Roo.lib.Point.prototype = new Roo.lib.Region();
3100 /*
3101  * Portions of this file are based on pieces of Yahoo User Interface Library
3102  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3103  * YUI licensed under the BSD License:
3104  * http://developer.yahoo.net/yui/license.txt
3105  * <script type="text/javascript">
3106  *
3107  */
3108  
3109 (function() {   
3110
3111     Roo.lib.Anim = {
3112         scroll : function(el, args, duration, easing, cb, scope) {
3113             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3114         },
3115
3116         motion : function(el, args, duration, easing, cb, scope) {
3117             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3118         },
3119
3120         color : function(el, args, duration, easing, cb, scope) {
3121             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3122         },
3123
3124         run : function(el, args, duration, easing, cb, scope, type) {
3125             type = type || Roo.lib.AnimBase;
3126             if (typeof easing == "string") {
3127                 easing = Roo.lib.Easing[easing];
3128             }
3129             var anim = new type(el, args, duration, easing);
3130             anim.animateX(function() {
3131                 Roo.callback(cb, scope);
3132             });
3133             return anim;
3134         }
3135     };
3136 })();/*
3137  * Portions of this file are based on pieces of Yahoo User Interface Library
3138  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3139  * YUI licensed under the BSD License:
3140  * http://developer.yahoo.net/yui/license.txt
3141  * <script type="text/javascript">
3142  *
3143  */
3144
3145 (function() {    
3146     var libFlyweight;
3147     
3148     function fly(el) {
3149         if (!libFlyweight) {
3150             libFlyweight = new Roo.Element.Flyweight();
3151         }
3152         libFlyweight.dom = el;
3153         return libFlyweight;
3154     }
3155
3156     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3157     
3158    
3159     
3160     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3161         if (el) {
3162             this.init(el, attributes, duration, method);
3163         }
3164     };
3165
3166     Roo.lib.AnimBase.fly = fly;
3167     
3168     
3169     
3170     Roo.lib.AnimBase.prototype = {
3171
3172         toString: function() {
3173             var el = this.getEl();
3174             var id = el.id || el.tagName;
3175             return ("Anim " + id);
3176         },
3177
3178         patterns: {
3179             noNegatives:        /width|height|opacity|padding/i,
3180             offsetAttribute:  /^((width|height)|(top|left))$/,
3181             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3182             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3183         },
3184
3185
3186         doMethod: function(attr, start, end) {
3187             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3188         },
3189
3190
3191         setAttribute: function(attr, val, unit) {
3192             if (this.patterns.noNegatives.test(attr)) {
3193                 val = (val > 0) ? val : 0;
3194             }
3195
3196             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3197         },
3198
3199
3200         getAttribute: function(attr) {
3201             var el = this.getEl();
3202             var val = fly(el).getStyle(attr);
3203
3204             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3205                 return parseFloat(val);
3206             }
3207
3208             var a = this.patterns.offsetAttribute.exec(attr) || [];
3209             var pos = !!( a[3] );
3210             var box = !!( a[2] );
3211
3212
3213             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3214                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3215             } else {
3216                 val = 0;
3217             }
3218
3219             return val;
3220         },
3221
3222
3223         getDefaultUnit: function(attr) {
3224             if (this.patterns.defaultUnit.test(attr)) {
3225                 return 'px';
3226             }
3227
3228             return '';
3229         },
3230
3231         animateX : function(callback, scope) {
3232             var f = function() {
3233                 this.onComplete.removeListener(f);
3234                 if (typeof callback == "function") {
3235                     callback.call(scope || this, this);
3236                 }
3237             };
3238             this.onComplete.addListener(f, this);
3239             this.animate();
3240         },
3241
3242
3243         setRuntimeAttribute: function(attr) {
3244             var start;
3245             var end;
3246             var attributes = this.attributes;
3247
3248             this.runtimeAttributes[attr] = {};
3249
3250             var isset = function(prop) {
3251                 return (typeof prop !== 'undefined');
3252             };
3253
3254             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3255                 return false;
3256             }
3257
3258             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3259
3260
3261             if (isset(attributes[attr]['to'])) {
3262                 end = attributes[attr]['to'];
3263             } else if (isset(attributes[attr]['by'])) {
3264                 if (start.constructor == Array) {
3265                     end = [];
3266                     for (var i = 0, len = start.length; i < len; ++i) {
3267                         end[i] = start[i] + attributes[attr]['by'][i];
3268                     }
3269                 } else {
3270                     end = start + attributes[attr]['by'];
3271                 }
3272             }
3273
3274             this.runtimeAttributes[attr].start = start;
3275             this.runtimeAttributes[attr].end = end;
3276
3277
3278             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3279         },
3280
3281
3282         init: function(el, attributes, duration, method) {
3283
3284             var isAnimated = false;
3285
3286
3287             var startTime = null;
3288
3289
3290             var actualFrames = 0;
3291
3292
3293             el = Roo.getDom(el);
3294
3295
3296             this.attributes = attributes || {};
3297
3298
3299             this.duration = duration || 1;
3300
3301
3302             this.method = method || Roo.lib.Easing.easeNone;
3303
3304
3305             this.useSeconds = true;
3306
3307
3308             this.currentFrame = 0;
3309
3310
3311             this.totalFrames = Roo.lib.AnimMgr.fps;
3312
3313
3314             this.getEl = function() {
3315                 return el;
3316             };
3317
3318
3319             this.isAnimated = function() {
3320                 return isAnimated;
3321             };
3322
3323
3324             this.getStartTime = function() {
3325                 return startTime;
3326             };
3327
3328             this.runtimeAttributes = {};
3329
3330
3331             this.animate = function() {
3332                 if (this.isAnimated()) {
3333                     return false;
3334                 }
3335
3336                 this.currentFrame = 0;
3337
3338                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3339
3340                 Roo.lib.AnimMgr.registerElement(this);
3341             };
3342
3343
3344             this.stop = function(finish) {
3345                 if (finish) {
3346                     this.currentFrame = this.totalFrames;
3347                     this._onTween.fire();
3348                 }
3349                 Roo.lib.AnimMgr.stop(this);
3350             };
3351
3352             var onStart = function() {
3353                 this.onStart.fire();
3354
3355                 this.runtimeAttributes = {};
3356                 for (var attr in this.attributes) {
3357                     this.setRuntimeAttribute(attr);
3358                 }
3359
3360                 isAnimated = true;
3361                 actualFrames = 0;
3362                 startTime = new Date();
3363             };
3364
3365
3366             var onTween = function() {
3367                 var data = {
3368                     duration: new Date() - this.getStartTime(),
3369                     currentFrame: this.currentFrame
3370                 };
3371
3372                 data.toString = function() {
3373                     return (
3374                             'duration: ' + data.duration +
3375                             ', currentFrame: ' + data.currentFrame
3376                             );
3377                 };
3378
3379                 this.onTween.fire(data);
3380
3381                 var runtimeAttributes = this.runtimeAttributes;
3382
3383                 for (var attr in runtimeAttributes) {
3384                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3385                 }
3386
3387                 actualFrames += 1;
3388             };
3389
3390             var onComplete = function() {
3391                 var actual_duration = (new Date() - startTime) / 1000 ;
3392
3393                 var data = {
3394                     duration: actual_duration,
3395                     frames: actualFrames,
3396                     fps: actualFrames / actual_duration
3397                 };
3398
3399                 data.toString = function() {
3400                     return (
3401                             'duration: ' + data.duration +
3402                             ', frames: ' + data.frames +
3403                             ', fps: ' + data.fps
3404                             );
3405                 };
3406
3407                 isAnimated = false;
3408                 actualFrames = 0;
3409                 this.onComplete.fire(data);
3410             };
3411
3412
3413             this._onStart = new Roo.util.Event(this);
3414             this.onStart = new Roo.util.Event(this);
3415             this.onTween = new Roo.util.Event(this);
3416             this._onTween = new Roo.util.Event(this);
3417             this.onComplete = new Roo.util.Event(this);
3418             this._onComplete = new Roo.util.Event(this);
3419             this._onStart.addListener(onStart);
3420             this._onTween.addListener(onTween);
3421             this._onComplete.addListener(onComplete);
3422         }
3423     };
3424 })();
3425 /*
3426  * Portions of this file are based on pieces of Yahoo User Interface Library
3427  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3428  * YUI licensed under the BSD License:
3429  * http://developer.yahoo.net/yui/license.txt
3430  * <script type="text/javascript">
3431  *
3432  */
3433
3434 Roo.lib.AnimMgr = new function() {
3435
3436     var thread = null;
3437
3438
3439     var queue = [];
3440
3441
3442     var tweenCount = 0;
3443
3444
3445     this.fps = 1000;
3446
3447
3448     this.delay = 1;
3449
3450
3451     this.registerElement = function(tween) {
3452         queue[queue.length] = tween;
3453         tweenCount += 1;
3454         tween._onStart.fire();
3455         this.start();
3456     };
3457
3458
3459     this.unRegister = function(tween, index) {
3460         tween._onComplete.fire();
3461         index = index || getIndex(tween);
3462         if (index != -1) {
3463             queue.splice(index, 1);
3464         }
3465
3466         tweenCount -= 1;
3467         if (tweenCount <= 0) {
3468             this.stop();
3469         }
3470     };
3471
3472
3473     this.start = function() {
3474         if (thread === null) {
3475             thread = setInterval(this.run, this.delay);
3476         }
3477     };
3478
3479
3480     this.stop = function(tween) {
3481         if (!tween) {
3482             clearInterval(thread);
3483
3484             for (var i = 0, len = queue.length; i < len; ++i) {
3485                 if (queue[0].isAnimated()) {
3486                     this.unRegister(queue[0], 0);
3487                 }
3488             }
3489
3490             queue = [];
3491             thread = null;
3492             tweenCount = 0;
3493         }
3494         else {
3495             this.unRegister(tween);
3496         }
3497     };
3498
3499
3500     this.run = function() {
3501         for (var i = 0, len = queue.length; i < len; ++i) {
3502             var tween = queue[i];
3503             if (!tween || !tween.isAnimated()) {
3504                 continue;
3505             }
3506
3507             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3508             {
3509                 tween.currentFrame += 1;
3510
3511                 if (tween.useSeconds) {
3512                     correctFrame(tween);
3513                 }
3514                 tween._onTween.fire();
3515             }
3516             else {
3517                 Roo.lib.AnimMgr.stop(tween, i);
3518             }
3519         }
3520     };
3521
3522     var getIndex = function(anim) {
3523         for (var i = 0, len = queue.length; i < len; ++i) {
3524             if (queue[i] == anim) {
3525                 return i;
3526             }
3527         }
3528         return -1;
3529     };
3530
3531
3532     var correctFrame = function(tween) {
3533         var frames = tween.totalFrames;
3534         var frame = tween.currentFrame;
3535         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3536         var elapsed = (new Date() - tween.getStartTime());
3537         var tweak = 0;
3538
3539         if (elapsed < tween.duration * 1000) {
3540             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3541         } else {
3542             tweak = frames - (frame + 1);
3543         }
3544         if (tweak > 0 && isFinite(tweak)) {
3545             if (tween.currentFrame + tweak >= frames) {
3546                 tweak = frames - (frame + 1);
3547             }
3548
3549             tween.currentFrame += tweak;
3550         }
3551     };
3552 };
3553
3554     /*
3555  * Portions of this file are based on pieces of Yahoo User Interface Library
3556  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3557  * YUI licensed under the BSD License:
3558  * http://developer.yahoo.net/yui/license.txt
3559  * <script type="text/javascript">
3560  *
3561  */
3562 Roo.lib.Bezier = new function() {
3563
3564         this.getPosition = function(points, t) {
3565             var n = points.length;
3566             var tmp = [];
3567
3568             for (var i = 0; i < n; ++i) {
3569                 tmp[i] = [points[i][0], points[i][1]];
3570             }
3571
3572             for (var j = 1; j < n; ++j) {
3573                 for (i = 0; i < n - j; ++i) {
3574                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3575                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3576                 }
3577             }
3578
3579             return [ tmp[0][0], tmp[0][1] ];
3580
3581         };
3582     };/*
3583  * Portions of this file are based on pieces of Yahoo User Interface Library
3584  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3585  * YUI licensed under the BSD License:
3586  * http://developer.yahoo.net/yui/license.txt
3587  * <script type="text/javascript">
3588  *
3589  */
3590 (function() {
3591
3592     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3593         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3594     };
3595
3596     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3597
3598     var fly = Roo.lib.AnimBase.fly;
3599     var Y = Roo.lib;
3600     var superclass = Y.ColorAnim.superclass;
3601     var proto = Y.ColorAnim.prototype;
3602
3603     proto.toString = function() {
3604         var el = this.getEl();
3605         var id = el.id || el.tagName;
3606         return ("ColorAnim " + id);
3607     };
3608
3609     proto.patterns.color = /color$/i;
3610     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3611     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3612     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3613     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3614
3615
3616     proto.parseColor = function(s) {
3617         if (s.length == 3) {
3618             return s;
3619         }
3620
3621         var c = this.patterns.hex.exec(s);
3622         if (c && c.length == 4) {
3623             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3624         }
3625
3626         c = this.patterns.rgb.exec(s);
3627         if (c && c.length == 4) {
3628             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3629         }
3630
3631         c = this.patterns.hex3.exec(s);
3632         if (c && c.length == 4) {
3633             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3634         }
3635
3636         return null;
3637     };
3638     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3639     proto.getAttribute = function(attr) {
3640         var el = this.getEl();
3641         if (this.patterns.color.test(attr)) {
3642             var val = fly(el).getStyle(attr);
3643
3644             if (this.patterns.transparent.test(val)) {
3645                 var parent = el.parentNode;
3646                 val = fly(parent).getStyle(attr);
3647
3648                 while (parent && this.patterns.transparent.test(val)) {
3649                     parent = parent.parentNode;
3650                     val = fly(parent).getStyle(attr);
3651                     if (parent.tagName.toUpperCase() == 'HTML') {
3652                         val = '#fff';
3653                     }
3654                 }
3655             }
3656         } else {
3657             val = superclass.getAttribute.call(this, attr);
3658         }
3659
3660         return val;
3661     };
3662     proto.getAttribute = function(attr) {
3663         var el = this.getEl();
3664         if (this.patterns.color.test(attr)) {
3665             var val = fly(el).getStyle(attr);
3666
3667             if (this.patterns.transparent.test(val)) {
3668                 var parent = el.parentNode;
3669                 val = fly(parent).getStyle(attr);
3670
3671                 while (parent && this.patterns.transparent.test(val)) {
3672                     parent = parent.parentNode;
3673                     val = fly(parent).getStyle(attr);
3674                     if (parent.tagName.toUpperCase() == 'HTML') {
3675                         val = '#fff';
3676                     }
3677                 }
3678             }
3679         } else {
3680             val = superclass.getAttribute.call(this, attr);
3681         }
3682
3683         return val;
3684     };
3685
3686     proto.doMethod = function(attr, start, end) {
3687         var val;
3688
3689         if (this.patterns.color.test(attr)) {
3690             val = [];
3691             for (var i = 0, len = start.length; i < len; ++i) {
3692                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3693             }
3694
3695             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3696         }
3697         else {
3698             val = superclass.doMethod.call(this, attr, start, end);
3699         }
3700
3701         return val;
3702     };
3703
3704     proto.setRuntimeAttribute = function(attr) {
3705         superclass.setRuntimeAttribute.call(this, attr);
3706
3707         if (this.patterns.color.test(attr)) {
3708             var attributes = this.attributes;
3709             var start = this.parseColor(this.runtimeAttributes[attr].start);
3710             var end = this.parseColor(this.runtimeAttributes[attr].end);
3711
3712             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3713                 end = this.parseColor(attributes[attr].by);
3714
3715                 for (var i = 0, len = start.length; i < len; ++i) {
3716                     end[i] = start[i] + end[i];
3717                 }
3718             }
3719
3720             this.runtimeAttributes[attr].start = start;
3721             this.runtimeAttributes[attr].end = end;
3722         }
3723     };
3724 })();
3725
3726 /*
3727  * Portions of this file are based on pieces of Yahoo User Interface Library
3728  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3729  * YUI licensed under the BSD License:
3730  * http://developer.yahoo.net/yui/license.txt
3731  * <script type="text/javascript">
3732  *
3733  */
3734 Roo.lib.Easing = {
3735
3736
3737     easeNone: function (t, b, c, d) {
3738         return c * t / d + b;
3739     },
3740
3741
3742     easeIn: function (t, b, c, d) {
3743         return c * (t /= d) * t + b;
3744     },
3745
3746
3747     easeOut: function (t, b, c, d) {
3748         return -c * (t /= d) * (t - 2) + b;
3749     },
3750
3751
3752     easeBoth: function (t, b, c, d) {
3753         if ((t /= d / 2) < 1) {
3754             return c / 2 * t * t + b;
3755         }
3756
3757         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3758     },
3759
3760
3761     easeInStrong: function (t, b, c, d) {
3762         return c * (t /= d) * t * t * t + b;
3763     },
3764
3765
3766     easeOutStrong: function (t, b, c, d) {
3767         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3768     },
3769
3770
3771     easeBothStrong: function (t, b, c, d) {
3772         if ((t /= d / 2) < 1) {
3773             return c / 2 * t * t * t * t + b;
3774         }
3775
3776         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3777     },
3778
3779
3780
3781     elasticIn: function (t, b, c, d, a, p) {
3782         if (t == 0) {
3783             return b;
3784         }
3785         if ((t /= d) == 1) {
3786             return b + c;
3787         }
3788         if (!p) {
3789             p = d * .3;
3790         }
3791
3792         if (!a || a < Math.abs(c)) {
3793             a = c;
3794             var s = p / 4;
3795         }
3796         else {
3797             var s = p / (2 * Math.PI) * Math.asin(c / a);
3798         }
3799
3800         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3801     },
3802
3803
3804     elasticOut: function (t, b, c, d, a, p) {
3805         if (t == 0) {
3806             return b;
3807         }
3808         if ((t /= d) == 1) {
3809             return b + c;
3810         }
3811         if (!p) {
3812             p = d * .3;
3813         }
3814
3815         if (!a || a < Math.abs(c)) {
3816             a = c;
3817             var s = p / 4;
3818         }
3819         else {
3820             var s = p / (2 * Math.PI) * Math.asin(c / a);
3821         }
3822
3823         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3824     },
3825
3826
3827     elasticBoth: function (t, b, c, d, a, p) {
3828         if (t == 0) {
3829             return b;
3830         }
3831
3832         if ((t /= d / 2) == 2) {
3833             return b + c;
3834         }
3835
3836         if (!p) {
3837             p = d * (.3 * 1.5);
3838         }
3839
3840         if (!a || a < Math.abs(c)) {
3841             a = c;
3842             var s = p / 4;
3843         }
3844         else {
3845             var s = p / (2 * Math.PI) * Math.asin(c / a);
3846         }
3847
3848         if (t < 1) {
3849             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3850                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3851         }
3852         return a * Math.pow(2, -10 * (t -= 1)) *
3853                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3854     },
3855
3856
3857
3858     backIn: function (t, b, c, d, s) {
3859         if (typeof s == 'undefined') {
3860             s = 1.70158;
3861         }
3862         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3863     },
3864
3865
3866     backOut: function (t, b, c, d, s) {
3867         if (typeof s == 'undefined') {
3868             s = 1.70158;
3869         }
3870         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3871     },
3872
3873
3874     backBoth: function (t, b, c, d, s) {
3875         if (typeof s == 'undefined') {
3876             s = 1.70158;
3877         }
3878
3879         if ((t /= d / 2 ) < 1) {
3880             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3881         }
3882         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3883     },
3884
3885
3886     bounceIn: function (t, b, c, d) {
3887         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3888     },
3889
3890
3891     bounceOut: function (t, b, c, d) {
3892         if ((t /= d) < (1 / 2.75)) {
3893             return c * (7.5625 * t * t) + b;
3894         } else if (t < (2 / 2.75)) {
3895             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3896         } else if (t < (2.5 / 2.75)) {
3897             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3898         }
3899         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3900     },
3901
3902
3903     bounceBoth: function (t, b, c, d) {
3904         if (t < d / 2) {
3905             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3906         }
3907         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3908     }
3909 };/*
3910  * Portions of this file are based on pieces of Yahoo User Interface Library
3911  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3912  * YUI licensed under the BSD License:
3913  * http://developer.yahoo.net/yui/license.txt
3914  * <script type="text/javascript">
3915  *
3916  */
3917     (function() {
3918         Roo.lib.Motion = function(el, attributes, duration, method) {
3919             if (el) {
3920                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3921             }
3922         };
3923
3924         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3925
3926
3927         var Y = Roo.lib;
3928         var superclass = Y.Motion.superclass;
3929         var proto = Y.Motion.prototype;
3930
3931         proto.toString = function() {
3932             var el = this.getEl();
3933             var id = el.id || el.tagName;
3934             return ("Motion " + id);
3935         };
3936
3937         proto.patterns.points = /^points$/i;
3938
3939         proto.setAttribute = function(attr, val, unit) {
3940             if (this.patterns.points.test(attr)) {
3941                 unit = unit || 'px';
3942                 superclass.setAttribute.call(this, 'left', val[0], unit);
3943                 superclass.setAttribute.call(this, 'top', val[1], unit);
3944             } else {
3945                 superclass.setAttribute.call(this, attr, val, unit);
3946             }
3947         };
3948
3949         proto.getAttribute = function(attr) {
3950             if (this.patterns.points.test(attr)) {
3951                 var val = [
3952                         superclass.getAttribute.call(this, 'left'),
3953                         superclass.getAttribute.call(this, 'top')
3954                         ];
3955             } else {
3956                 val = superclass.getAttribute.call(this, attr);
3957             }
3958
3959             return val;
3960         };
3961
3962         proto.doMethod = function(attr, start, end) {
3963             var val = null;
3964
3965             if (this.patterns.points.test(attr)) {
3966                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3967                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3968             } else {
3969                 val = superclass.doMethod.call(this, attr, start, end);
3970             }
3971             return val;
3972         };
3973
3974         proto.setRuntimeAttribute = function(attr) {
3975             if (this.patterns.points.test(attr)) {
3976                 var el = this.getEl();
3977                 var attributes = this.attributes;
3978                 var start;
3979                 var control = attributes['points']['control'] || [];
3980                 var end;
3981                 var i, len;
3982
3983                 if (control.length > 0 && !(control[0] instanceof Array)) {
3984                     control = [control];
3985                 } else {
3986                     var tmp = [];
3987                     for (i = 0,len = control.length; i < len; ++i) {
3988                         tmp[i] = control[i];
3989                     }
3990                     control = tmp;
3991                 }
3992
3993                 Roo.fly(el).position();
3994
3995                 if (isset(attributes['points']['from'])) {
3996                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3997                 }
3998                 else {
3999                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4000                 }
4001
4002                 start = this.getAttribute('points');
4003
4004
4005                 if (isset(attributes['points']['to'])) {
4006                     end = translateValues.call(this, attributes['points']['to'], start);
4007
4008                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4009                     for (i = 0,len = control.length; i < len; ++i) {
4010                         control[i] = translateValues.call(this, control[i], start);
4011                     }
4012
4013
4014                 } else if (isset(attributes['points']['by'])) {
4015                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4016
4017                     for (i = 0,len = control.length; i < len; ++i) {
4018                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4019                     }
4020                 }
4021
4022                 this.runtimeAttributes[attr] = [start];
4023
4024                 if (control.length > 0) {
4025                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4026                 }
4027
4028                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4029             }
4030             else {
4031                 superclass.setRuntimeAttribute.call(this, attr);
4032             }
4033         };
4034
4035         var translateValues = function(val, start) {
4036             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4037             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4038
4039             return val;
4040         };
4041
4042         var isset = function(prop) {
4043             return (typeof prop !== 'undefined');
4044         };
4045     })();
4046 /*
4047  * Portions of this file are based on pieces of Yahoo User Interface Library
4048  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4049  * YUI licensed under the BSD License:
4050  * http://developer.yahoo.net/yui/license.txt
4051  * <script type="text/javascript">
4052  *
4053  */
4054     (function() {
4055         Roo.lib.Scroll = function(el, attributes, duration, method) {
4056             if (el) {
4057                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4058             }
4059         };
4060
4061         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4062
4063
4064         var Y = Roo.lib;
4065         var superclass = Y.Scroll.superclass;
4066         var proto = Y.Scroll.prototype;
4067
4068         proto.toString = function() {
4069             var el = this.getEl();
4070             var id = el.id || el.tagName;
4071             return ("Scroll " + id);
4072         };
4073
4074         proto.doMethod = function(attr, start, end) {
4075             var val = null;
4076
4077             if (attr == 'scroll') {
4078                 val = [
4079                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4080                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4081                         ];
4082
4083             } else {
4084                 val = superclass.doMethod.call(this, attr, start, end);
4085             }
4086             return val;
4087         };
4088
4089         proto.getAttribute = function(attr) {
4090             var val = null;
4091             var el = this.getEl();
4092
4093             if (attr == 'scroll') {
4094                 val = [ el.scrollLeft, el.scrollTop ];
4095             } else {
4096                 val = superclass.getAttribute.call(this, attr);
4097             }
4098
4099             return val;
4100         };
4101
4102         proto.setAttribute = function(attr, val, unit) {
4103             var el = this.getEl();
4104
4105             if (attr == 'scroll') {
4106                 el.scrollLeft = val[0];
4107                 el.scrollTop = val[1];
4108             } else {
4109                 superclass.setAttribute.call(this, attr, val, unit);
4110             }
4111         };
4112     })();
4113 /*
4114  * Based on:
4115  * Ext JS Library 1.1.1
4116  * Copyright(c) 2006-2007, Ext JS, LLC.
4117  *
4118  * Originally Released Under LGPL - original licence link has changed is not relivant.
4119  *
4120  * Fork - LGPL
4121  * <script type="text/javascript">
4122  */
4123
4124
4125 // nasty IE9 hack - what a pile of crap that is..
4126
4127  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4128     Range.prototype.createContextualFragment = function (html) {
4129         var doc = window.document;
4130         var container = doc.createElement("div");
4131         container.innerHTML = html;
4132         var frag = doc.createDocumentFragment(), n;
4133         while ((n = container.firstChild)) {
4134             frag.appendChild(n);
4135         }
4136         return frag;
4137     };
4138 }
4139
4140 /**
4141  * @class Roo.DomHelper
4142  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4143  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4144  * @singleton
4145  */
4146 Roo.DomHelper = function(){
4147     var tempTableEl = null;
4148     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4149     var tableRe = /^table|tbody|tr|td$/i;
4150     var xmlns = {};
4151     // build as innerHTML where available
4152     /** @ignore */
4153     var createHtml = function(o){
4154         if(typeof o == 'string'){
4155             return o;
4156         }
4157         var b = "";
4158         if(!o.tag){
4159             o.tag = "div";
4160         }
4161         b += "<" + o.tag;
4162         for(var attr in o){
4163             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4164             if(attr == "style"){
4165                 var s = o["style"];
4166                 if(typeof s == "function"){
4167                     s = s.call();
4168                 }
4169                 if(typeof s == "string"){
4170                     b += ' style="' + s + '"';
4171                 }else if(typeof s == "object"){
4172                     b += ' style="';
4173                     for(var key in s){
4174                         if(typeof s[key] != "function"){
4175                             b += key + ":" + s[key] + ";";
4176                         }
4177                     }
4178                     b += '"';
4179                 }
4180             }else{
4181                 if(attr == "cls"){
4182                     b += ' class="' + o["cls"] + '"';
4183                 }else if(attr == "htmlFor"){
4184                     b += ' for="' + o["htmlFor"] + '"';
4185                 }else{
4186                     b += " " + attr + '="' + o[attr] + '"';
4187                 }
4188             }
4189         }
4190         if(emptyTags.test(o.tag)){
4191             b += "/>";
4192         }else{
4193             b += ">";
4194             var cn = o.children || o.cn;
4195             if(cn){
4196                 //http://bugs.kde.org/show_bug.cgi?id=71506
4197                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                     for(var i = 0, len = cn.length; i < len; i++) {
4199                         b += createHtml(cn[i], b);
4200                     }
4201                 }else{
4202                     b += createHtml(cn, b);
4203                 }
4204             }
4205             if(o.html){
4206                 b += o.html;
4207             }
4208             b += "</" + o.tag + ">";
4209         }
4210         return b;
4211     };
4212
4213     // build as dom
4214     /** @ignore */
4215     var createDom = function(o, parentNode){
4216          
4217         // defininition craeted..
4218         var ns = false;
4219         if (o.ns && o.ns != 'html') {
4220                
4221             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4222                 xmlns[o.ns] = o.xmlns;
4223                 ns = o.xmlns;
4224             }
4225             if (typeof(xmlns[o.ns]) == 'undefined') {
4226                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4227             }
4228             ns = xmlns[o.ns];
4229         }
4230         
4231         
4232         if (typeof(o) == 'string') {
4233             return parentNode.appendChild(document.createTextNode(o));
4234         }
4235         o.tag = o.tag || div;
4236         if (o.ns && Roo.isIE) {
4237             ns = false;
4238             o.tag = o.ns + ':' + o.tag;
4239             
4240         }
4241         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4242         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4243         for(var attr in o){
4244             
4245             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4246                     attr == "style" || typeof o[attr] == "function") { continue; }
4247                     
4248             if(attr=="cls" && Roo.isIE){
4249                 el.className = o["cls"];
4250             }else{
4251                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4252                 else { 
4253                     el[attr] = o[attr];
4254                 }
4255             }
4256         }
4257         Roo.DomHelper.applyStyles(el, o.style);
4258         var cn = o.children || o.cn;
4259         if(cn){
4260             //http://bugs.kde.org/show_bug.cgi?id=71506
4261              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4262                 for(var i = 0, len = cn.length; i < len; i++) {
4263                     createDom(cn[i], el);
4264                 }
4265             }else{
4266                 createDom(cn, el);
4267             }
4268         }
4269         if(o.html){
4270             el.innerHTML = o.html;
4271         }
4272         if(parentNode){
4273            parentNode.appendChild(el);
4274         }
4275         return el;
4276     };
4277
4278     var ieTable = function(depth, s, h, e){
4279         tempTableEl.innerHTML = [s, h, e].join('');
4280         var i = -1, el = tempTableEl;
4281         while(++i < depth){
4282             el = el.firstChild;
4283         }
4284         return el;
4285     };
4286
4287     // kill repeat to save bytes
4288     var ts = '<table>',
4289         te = '</table>',
4290         tbs = ts+'<tbody>',
4291         tbe = '</tbody>'+te,
4292         trs = tbs + '<tr>',
4293         tre = '</tr>'+tbe;
4294
4295     /**
4296      * @ignore
4297      * Nasty code for IE's broken table implementation
4298      */
4299     var insertIntoTable = function(tag, where, el, html){
4300         if(!tempTableEl){
4301             tempTableEl = document.createElement('div');
4302         }
4303         var node;
4304         var before = null;
4305         if(tag == 'td'){
4306             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4307                 return;
4308             }
4309             if(where == 'beforebegin'){
4310                 before = el;
4311                 el = el.parentNode;
4312             } else{
4313                 before = el.nextSibling;
4314                 el = el.parentNode;
4315             }
4316             node = ieTable(4, trs, html, tre);
4317         }
4318         else if(tag == 'tr'){
4319             if(where == 'beforebegin'){
4320                 before = el;
4321                 el = el.parentNode;
4322                 node = ieTable(3, tbs, html, tbe);
4323             } else if(where == 'afterend'){
4324                 before = el.nextSibling;
4325                 el = el.parentNode;
4326                 node = ieTable(3, tbs, html, tbe);
4327             } else{ // INTO a TR
4328                 if(where == 'afterbegin'){
4329                     before = el.firstChild;
4330                 }
4331                 node = ieTable(4, trs, html, tre);
4332             }
4333         } else if(tag == 'tbody'){
4334             if(where == 'beforebegin'){
4335                 before = el;
4336                 el = el.parentNode;
4337                 node = ieTable(2, ts, html, te);
4338             } else if(where == 'afterend'){
4339                 before = el.nextSibling;
4340                 el = el.parentNode;
4341                 node = ieTable(2, ts, html, te);
4342             } else{
4343                 if(where == 'afterbegin'){
4344                     before = el.firstChild;
4345                 }
4346                 node = ieTable(3, tbs, html, tbe);
4347             }
4348         } else{ // TABLE
4349             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4350                 return;
4351             }
4352             if(where == 'afterbegin'){
4353                 before = el.firstChild;
4354             }
4355             node = ieTable(2, ts, html, te);
4356         }
4357         el.insertBefore(node, before);
4358         return node;
4359     };
4360
4361     return {
4362     /** True to force the use of DOM instead of html fragments @type Boolean */
4363     useDom : false,
4364
4365     /**
4366      * Returns the markup for the passed Element(s) config
4367      * @param {Object} o The Dom object spec (and children)
4368      * @return {String}
4369      */
4370     markup : function(o){
4371         return createHtml(o);
4372     },
4373
4374     /**
4375      * Applies a style specification to an element
4376      * @param {String/HTMLElement} el The element to apply styles to
4377      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4378      * a function which returns such a specification.
4379      */
4380     applyStyles : function(el, styles){
4381         if(styles){
4382            el = Roo.fly(el);
4383            if(typeof styles == "string"){
4384                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4385                var matches;
4386                while ((matches = re.exec(styles)) != null){
4387                    el.setStyle(matches[1], matches[2]);
4388                }
4389            }else if (typeof styles == "object"){
4390                for (var style in styles){
4391                   el.setStyle(style, styles[style]);
4392                }
4393            }else if (typeof styles == "function"){
4394                 Roo.DomHelper.applyStyles(el, styles.call());
4395            }
4396         }
4397     },
4398
4399     /**
4400      * Inserts an HTML fragment into the Dom
4401      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4402      * @param {HTMLElement} el The context element
4403      * @param {String} html The HTML fragmenet
4404      * @return {HTMLElement} The new node
4405      */
4406     insertHtml : function(where, el, html){
4407         where = where.toLowerCase();
4408         if(el.insertAdjacentHTML){
4409             if(tableRe.test(el.tagName)){
4410                 var rs;
4411                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4412                     return rs;
4413                 }
4414             }
4415             switch(where){
4416                 case "beforebegin":
4417                     el.insertAdjacentHTML('BeforeBegin', html);
4418                     return el.previousSibling;
4419                 case "afterbegin":
4420                     el.insertAdjacentHTML('AfterBegin', html);
4421                     return el.firstChild;
4422                 case "beforeend":
4423                     el.insertAdjacentHTML('BeforeEnd', html);
4424                     return el.lastChild;
4425                 case "afterend":
4426                     el.insertAdjacentHTML('AfterEnd', html);
4427                     return el.nextSibling;
4428             }
4429             throw 'Illegal insertion point -> "' + where + '"';
4430         }
4431         var range = el.ownerDocument.createRange();
4432         var frag;
4433         switch(where){
4434              case "beforebegin":
4435                 range.setStartBefore(el);
4436                 frag = range.createContextualFragment(html);
4437                 el.parentNode.insertBefore(frag, el);
4438                 return el.previousSibling;
4439              case "afterbegin":
4440                 if(el.firstChild){
4441                     range.setStartBefore(el.firstChild);
4442                     frag = range.createContextualFragment(html);
4443                     el.insertBefore(frag, el.firstChild);
4444                     return el.firstChild;
4445                 }else{
4446                     el.innerHTML = html;
4447                     return el.firstChild;
4448                 }
4449             case "beforeend":
4450                 if(el.lastChild){
4451                     range.setStartAfter(el.lastChild);
4452                     frag = range.createContextualFragment(html);
4453                     el.appendChild(frag);
4454                     return el.lastChild;
4455                 }else{
4456                     el.innerHTML = html;
4457                     return el.lastChild;
4458                 }
4459             case "afterend":
4460                 range.setStartAfter(el);
4461                 frag = range.createContextualFragment(html);
4462                 el.parentNode.insertBefore(frag, el.nextSibling);
4463                 return el.nextSibling;
4464             }
4465             throw 'Illegal insertion point -> "' + where + '"';
4466     },
4467
4468     /**
4469      * Creates new Dom element(s) and inserts them before el
4470      * @param {String/HTMLElement/Element} el The context element
4471      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4472      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4473      * @return {HTMLElement/Roo.Element} The new node
4474      */
4475     insertBefore : function(el, o, returnElement){
4476         return this.doInsert(el, o, returnElement, "beforeBegin");
4477     },
4478
4479     /**
4480      * Creates new Dom element(s) and inserts them after el
4481      * @param {String/HTMLElement/Element} el The context element
4482      * @param {Object} o The Dom object spec (and children)
4483      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4484      * @return {HTMLElement/Roo.Element} The new node
4485      */
4486     insertAfter : function(el, o, returnElement){
4487         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4488     },
4489
4490     /**
4491      * Creates new Dom element(s) and inserts them as the first child of el
4492      * @param {String/HTMLElement/Element} el The context element
4493      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4494      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4495      * @return {HTMLElement/Roo.Element} The new node
4496      */
4497     insertFirst : function(el, o, returnElement){
4498         return this.doInsert(el, o, returnElement, "afterBegin");
4499     },
4500
4501     // private
4502     doInsert : function(el, o, returnElement, pos, sibling){
4503         el = Roo.getDom(el);
4504         var newNode;
4505         if(this.useDom || o.ns){
4506             newNode = createDom(o, null);
4507             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4508         }else{
4509             var html = createHtml(o);
4510             newNode = this.insertHtml(pos, el, html);
4511         }
4512         return returnElement ? Roo.get(newNode, true) : newNode;
4513     },
4514
4515     /**
4516      * Creates new Dom element(s) and appends them to el
4517      * @param {String/HTMLElement/Element} el The context element
4518      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4519      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4520      * @return {HTMLElement/Roo.Element} The new node
4521      */
4522     append : function(el, o, returnElement){
4523         el = Roo.getDom(el);
4524         var newNode;
4525         if(this.useDom || o.ns){
4526             newNode = createDom(o, null);
4527             el.appendChild(newNode);
4528         }else{
4529             var html = createHtml(o);
4530             newNode = this.insertHtml("beforeEnd", el, html);
4531         }
4532         return returnElement ? Roo.get(newNode, true) : newNode;
4533     },
4534
4535     /**
4536      * Creates new Dom element(s) and overwrites the contents of el with them
4537      * @param {String/HTMLElement/Element} el The context element
4538      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4539      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4540      * @return {HTMLElement/Roo.Element} The new node
4541      */
4542     overwrite : function(el, o, returnElement){
4543         el = Roo.getDom(el);
4544         if (o.ns) {
4545           
4546             while (el.childNodes.length) {
4547                 el.removeChild(el.firstChild);
4548             }
4549             createDom(o, el);
4550         } else {
4551             el.innerHTML = createHtml(o);   
4552         }
4553         
4554         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4555     },
4556
4557     /**
4558      * Creates a new Roo.DomHelper.Template from the Dom object spec
4559      * @param {Object} o The Dom object spec (and children)
4560      * @return {Roo.DomHelper.Template} The new template
4561      */
4562     createTemplate : function(o){
4563         var html = createHtml(o);
4564         return new Roo.Template(html);
4565     }
4566     };
4567 }();
4568 /*
4569  * Based on:
4570  * Ext JS Library 1.1.1
4571  * Copyright(c) 2006-2007, Ext JS, LLC.
4572  *
4573  * Originally Released Under LGPL - original licence link has changed is not relivant.
4574  *
4575  * Fork - LGPL
4576  * <script type="text/javascript">
4577  */
4578  
4579 /**
4580 * @class Roo.Template
4581 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4582 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4583 * Usage:
4584 <pre><code>
4585 var t = new Roo.Template({
4586     html :  '&lt;div name="{id}"&gt;' + 
4587         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4588         '&lt;/div&gt;',
4589     myformat: function (value, allValues) {
4590         return 'XX' + value;
4591     }
4592 });
4593 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4594 </code></pre>
4595 * For more information see this blog post with examples:
4596 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4597      - Create Elements using DOM, HTML fragments and Templates</a>. 
4598 * @constructor
4599 * @param {Object} cfg - Configuration object.
4600 */
4601 Roo.Template = function(cfg){
4602     // BC!
4603     if(cfg instanceof Array){
4604         cfg = cfg.join("");
4605     }else if(arguments.length > 1){
4606         cfg = Array.prototype.join.call(arguments, "");
4607     }
4608     
4609     
4610     if (typeof(cfg) == 'object') {
4611         Roo.apply(this,cfg)
4612     } else {
4613         // bc
4614         this.html = cfg;
4615     }
4616     if (this.url) {
4617         this.load();
4618     }
4619     
4620 };
4621 Roo.Template.prototype = {
4622     
4623     /**
4624      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4625      *                    it should be fixed so that template is observable...
4626      */
4627     url : false,
4628     /**
4629      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4630      */
4631     html : '',
4632     /**
4633      * Returns an HTML fragment of this template with the specified values applied.
4634      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4635      * @return {String} The HTML fragment
4636      */
4637     applyTemplate : function(values){
4638         try {
4639            
4640             if(this.compiled){
4641                 return this.compiled(values);
4642             }
4643             var useF = this.disableFormats !== true;
4644             var fm = Roo.util.Format, tpl = this;
4645             var fn = function(m, name, format, args){
4646                 if(format && useF){
4647                     if(format.substr(0, 5) == "this."){
4648                         return tpl.call(format.substr(5), values[name], values);
4649                     }else{
4650                         if(args){
4651                             // quoted values are required for strings in compiled templates, 
4652                             // but for non compiled we need to strip them
4653                             // quoted reversed for jsmin
4654                             var re = /^\s*['"](.*)["']\s*$/;
4655                             args = args.split(',');
4656                             for(var i = 0, len = args.length; i < len; i++){
4657                                 args[i] = args[i].replace(re, "$1");
4658                             }
4659                             args = [values[name]].concat(args);
4660                         }else{
4661                             args = [values[name]];
4662                         }
4663                         return fm[format].apply(fm, args);
4664                     }
4665                 }else{
4666                     return values[name] !== undefined ? values[name] : "";
4667                 }
4668             };
4669             return this.html.replace(this.re, fn);
4670         } catch (e) {
4671             Roo.log(e);
4672             throw e;
4673         }
4674          
4675     },
4676     
4677     loading : false,
4678       
4679     load : function ()
4680     {
4681          
4682         if (this.loading) {
4683             return;
4684         }
4685         var _t = this;
4686         
4687         this.loading = true;
4688         this.compiled = false;
4689         
4690         var cx = new Roo.data.Connection();
4691         cx.request({
4692             url : this.url,
4693             method : 'GET',
4694             success : function (response) {
4695                 _t.loading = false;
4696                 _t.html = response.responseText;
4697                 _t.url = false;
4698                 _t.compile();
4699              },
4700             failure : function(response) {
4701                 Roo.log("Template failed to load from " + _t.url);
4702                 _t.loading = false;
4703             }
4704         });
4705     },
4706
4707     /**
4708      * Sets the HTML used as the template and optionally compiles it.
4709      * @param {String} html
4710      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4711      * @return {Roo.Template} this
4712      */
4713     set : function(html, compile){
4714         this.html = html;
4715         this.compiled = null;
4716         if(compile){
4717             this.compile();
4718         }
4719         return this;
4720     },
4721     
4722     /**
4723      * True to disable format functions (defaults to false)
4724      * @type Boolean
4725      */
4726     disableFormats : false,
4727     
4728     /**
4729     * The regular expression used to match template variables 
4730     * @type RegExp
4731     * @property 
4732     */
4733     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4734     
4735     /**
4736      * Compiles the template into an internal function, eliminating the RegEx overhead.
4737      * @return {Roo.Template} this
4738      */
4739     compile : function(){
4740         var fm = Roo.util.Format;
4741         var useF = this.disableFormats !== true;
4742         var sep = Roo.isGecko ? "+" : ",";
4743         var fn = function(m, name, format, args){
4744             if(format && useF){
4745                 args = args ? ',' + args : "";
4746                 if(format.substr(0, 5) != "this."){
4747                     format = "fm." + format + '(';
4748                 }else{
4749                     format = 'this.call("'+ format.substr(5) + '", ';
4750                     args = ", values";
4751                 }
4752             }else{
4753                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4754             }
4755             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4756         };
4757         var body;
4758         // branched to use + in gecko and [].join() in others
4759         if(Roo.isGecko){
4760             body = "this.compiled = function(values){ return '" +
4761                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4762                     "';};";
4763         }else{
4764             body = ["this.compiled = function(values){ return ['"];
4765             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4766             body.push("'].join('');};");
4767             body = body.join('');
4768         }
4769         /**
4770          * eval:var:values
4771          * eval:var:fm
4772          */
4773         eval(body);
4774         return this;
4775     },
4776     
4777     // private function used to call members
4778     call : function(fnName, value, allValues){
4779         return this[fnName](value, allValues);
4780     },
4781     
4782     /**
4783      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4784      * @param {String/HTMLElement/Roo.Element} el The context element
4785      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4786      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4787      * @return {HTMLElement/Roo.Element} The new node or Element
4788      */
4789     insertFirst: function(el, values, returnElement){
4790         return this.doInsert('afterBegin', el, values, returnElement);
4791     },
4792
4793     /**
4794      * Applies the supplied values to the template and inserts the new node(s) before el.
4795      * @param {String/HTMLElement/Roo.Element} el The context element
4796      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4797      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4798      * @return {HTMLElement/Roo.Element} The new node or Element
4799      */
4800     insertBefore: function(el, values, returnElement){
4801         return this.doInsert('beforeBegin', el, values, returnElement);
4802     },
4803
4804     /**
4805      * Applies the supplied values to the template and inserts the new node(s) after el.
4806      * @param {String/HTMLElement/Roo.Element} el The context element
4807      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4808      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4809      * @return {HTMLElement/Roo.Element} The new node or Element
4810      */
4811     insertAfter : function(el, values, returnElement){
4812         return this.doInsert('afterEnd', el, values, returnElement);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and appends the new node(s) to el.
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     append : function(el, values, returnElement){
4823         return this.doInsert('beforeEnd', el, values, returnElement);
4824     },
4825
4826     doInsert : function(where, el, values, returnEl){
4827         el = Roo.getDom(el);
4828         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4829         return returnEl ? Roo.get(newNode, true) : newNode;
4830     },
4831
4832     /**
4833      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4834      * @param {String/HTMLElement/Roo.Element} el The context element
4835      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4836      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4837      * @return {HTMLElement/Roo.Element} The new node or Element
4838      */
4839     overwrite : function(el, values, returnElement){
4840         el = Roo.getDom(el);
4841         el.innerHTML = this.applyTemplate(values);
4842         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4843     }
4844 };
4845 /**
4846  * Alias for {@link #applyTemplate}
4847  * @method
4848  */
4849 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4850
4851 // backwards compat
4852 Roo.DomHelper.Template = Roo.Template;
4853
4854 /**
4855  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4856  * @param {String/HTMLElement} el A DOM element or its id
4857  * @returns {Roo.Template} The created template
4858  * @static
4859  */
4860 Roo.Template.from = function(el){
4861     el = Roo.getDom(el);
4862     return new Roo.Template(el.value || el.innerHTML);
4863 };/*
4864  * Based on:
4865  * Ext JS Library 1.1.1
4866  * Copyright(c) 2006-2007, Ext JS, LLC.
4867  *
4868  * Originally Released Under LGPL - original licence link has changed is not relivant.
4869  *
4870  * Fork - LGPL
4871  * <script type="text/javascript">
4872  */
4873  
4874
4875 /*
4876  * This is code is also distributed under MIT license for use
4877  * with jQuery and prototype JavaScript libraries.
4878  */
4879 /**
4880  * @class Roo.DomQuery
4881 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4882 <p>
4883 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4884
4885 <p>
4886 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4887 </p>
4888 <h4>Element Selectors:</h4>
4889 <ul class="list">
4890     <li> <b>*</b> any element</li>
4891     <li> <b>E</b> an element with the tag E</li>
4892     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4893     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4894     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4895     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4896 </ul>
4897 <h4>Attribute Selectors:</h4>
4898 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4899 <ul class="list">
4900     <li> <b>E[foo]</b> has an attribute "foo"</li>
4901     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4902     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4903     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4904     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4905     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4906     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4907 </ul>
4908 <h4>Pseudo Classes:</h4>
4909 <ul class="list">
4910     <li> <b>E:first-child</b> E is the first child of its parent</li>
4911     <li> <b>E:last-child</b> E is the last child of its parent</li>
4912     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4913     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4914     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4915     <li> <b>E:only-child</b> E is the only child of its parent</li>
4916     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4917     <li> <b>E:first</b> the first E in the resultset</li>
4918     <li> <b>E:last</b> the last E in the resultset</li>
4919     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4920     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4921     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4922     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4923     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4924     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4925     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4926     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4927     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4928 </ul>
4929 <h4>CSS Value Selectors:</h4>
4930 <ul class="list">
4931     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4932     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4933     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4934     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4935     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4936     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4937 </ul>
4938  * @singleton
4939  */
4940 Roo.DomQuery = function(){
4941     var cache = {}, simpleCache = {}, valueCache = {};
4942     var nonSpace = /\S/;
4943     var trimRe = /^\s+|\s+$/g;
4944     var tplRe = /\{(\d+)\}/g;
4945     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4946     var tagTokenRe = /^(#)?([\w-\*]+)/;
4947     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4948
4949     function child(p, index){
4950         var i = 0;
4951         var n = p.firstChild;
4952         while(n){
4953             if(n.nodeType == 1){
4954                if(++i == index){
4955                    return n;
4956                }
4957             }
4958             n = n.nextSibling;
4959         }
4960         return null;
4961     };
4962
4963     function next(n){
4964         while((n = n.nextSibling) && n.nodeType != 1);
4965         return n;
4966     };
4967
4968     function prev(n){
4969         while((n = n.previousSibling) && n.nodeType != 1);
4970         return n;
4971     };
4972
4973     function children(d){
4974         var n = d.firstChild, ni = -1;
4975             while(n){
4976                 var nx = n.nextSibling;
4977                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4978                     d.removeChild(n);
4979                 }else{
4980                     n.nodeIndex = ++ni;
4981                 }
4982                 n = nx;
4983             }
4984             return this;
4985         };
4986
4987     function byClassName(c, a, v){
4988         if(!v){
4989             return c;
4990         }
4991         var r = [], ri = -1, cn;
4992         for(var i = 0, ci; ci = c[i]; i++){
4993             if((' '+ci.className+' ').indexOf(v) != -1){
4994                 r[++ri] = ci;
4995             }
4996         }
4997         return r;
4998     };
4999
5000     function attrValue(n, attr){
5001         if(!n.tagName && typeof n.length != "undefined"){
5002             n = n[0];
5003         }
5004         if(!n){
5005             return null;
5006         }
5007         if(attr == "for"){
5008             return n.htmlFor;
5009         }
5010         if(attr == "class" || attr == "className"){
5011             return n.className;
5012         }
5013         return n.getAttribute(attr) || n[attr];
5014
5015     };
5016
5017     function getNodes(ns, mode, tagName){
5018         var result = [], ri = -1, cs;
5019         if(!ns){
5020             return result;
5021         }
5022         tagName = tagName || "*";
5023         if(typeof ns.getElementsByTagName != "undefined"){
5024             ns = [ns];
5025         }
5026         if(!mode){
5027             for(var i = 0, ni; ni = ns[i]; i++){
5028                 cs = ni.getElementsByTagName(tagName);
5029                 for(var j = 0, ci; ci = cs[j]; j++){
5030                     result[++ri] = ci;
5031                 }
5032             }
5033         }else if(mode == "/" || mode == ">"){
5034             var utag = tagName.toUpperCase();
5035             for(var i = 0, ni, cn; ni = ns[i]; i++){
5036                 cn = ni.children || ni.childNodes;
5037                 for(var j = 0, cj; cj = cn[j]; j++){
5038                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5039                         result[++ri] = cj;
5040                     }
5041                 }
5042             }
5043         }else if(mode == "+"){
5044             var utag = tagName.toUpperCase();
5045             for(var i = 0, n; n = ns[i]; i++){
5046                 while((n = n.nextSibling) && n.nodeType != 1);
5047                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5048                     result[++ri] = n;
5049                 }
5050             }
5051         }else if(mode == "~"){
5052             for(var i = 0, n; n = ns[i]; i++){
5053                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5054                 if(n){
5055                     result[++ri] = n;
5056                 }
5057             }
5058         }
5059         return result;
5060     };
5061
5062     function concat(a, b){
5063         if(b.slice){
5064             return a.concat(b);
5065         }
5066         for(var i = 0, l = b.length; i < l; i++){
5067             a[a.length] = b[i];
5068         }
5069         return a;
5070     }
5071
5072     function byTag(cs, tagName){
5073         if(cs.tagName || cs == document){
5074             cs = [cs];
5075         }
5076         if(!tagName){
5077             return cs;
5078         }
5079         var r = [], ri = -1;
5080         tagName = tagName.toLowerCase();
5081         for(var i = 0, ci; ci = cs[i]; i++){
5082             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5083                 r[++ri] = ci;
5084             }
5085         }
5086         return r;
5087     };
5088
5089     function byId(cs, attr, id){
5090         if(cs.tagName || cs == document){
5091             cs = [cs];
5092         }
5093         if(!id){
5094             return cs;
5095         }
5096         var r = [], ri = -1;
5097         for(var i = 0,ci; ci = cs[i]; i++){
5098             if(ci && ci.id == id){
5099                 r[++ri] = ci;
5100                 return r;
5101             }
5102         }
5103         return r;
5104     };
5105
5106     function byAttribute(cs, attr, value, op, custom){
5107         var r = [], ri = -1, st = custom=="{";
5108         var f = Roo.DomQuery.operators[op];
5109         for(var i = 0, ci; ci = cs[i]; i++){
5110             var a;
5111             if(st){
5112                 a = Roo.DomQuery.getStyle(ci, attr);
5113             }
5114             else if(attr == "class" || attr == "className"){
5115                 a = ci.className;
5116             }else if(attr == "for"){
5117                 a = ci.htmlFor;
5118             }else if(attr == "href"){
5119                 a = ci.getAttribute("href", 2);
5120             }else{
5121                 a = ci.getAttribute(attr);
5122             }
5123             if((f && f(a, value)) || (!f && a)){
5124                 r[++ri] = ci;
5125             }
5126         }
5127         return r;
5128     };
5129
5130     function byPseudo(cs, name, value){
5131         return Roo.DomQuery.pseudos[name](cs, value);
5132     };
5133
5134     // This is for IE MSXML which does not support expandos.
5135     // IE runs the same speed using setAttribute, however FF slows way down
5136     // and Safari completely fails so they need to continue to use expandos.
5137     var isIE = window.ActiveXObject ? true : false;
5138
5139     // this eval is stop the compressor from
5140     // renaming the variable to something shorter
5141     
5142     /** eval:var:batch */
5143     var batch = 30803; 
5144
5145     var key = 30803;
5146
5147     function nodupIEXml(cs){
5148         var d = ++key;
5149         cs[0].setAttribute("_nodup", d);
5150         var r = [cs[0]];
5151         for(var i = 1, len = cs.length; i < len; i++){
5152             var c = cs[i];
5153             if(!c.getAttribute("_nodup") != d){
5154                 c.setAttribute("_nodup", d);
5155                 r[r.length] = c;
5156             }
5157         }
5158         for(var i = 0, len = cs.length; i < len; i++){
5159             cs[i].removeAttribute("_nodup");
5160         }
5161         return r;
5162     }
5163
5164     function nodup(cs){
5165         if(!cs){
5166             return [];
5167         }
5168         var len = cs.length, c, i, r = cs, cj, ri = -1;
5169         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5170             return cs;
5171         }
5172         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5173             return nodupIEXml(cs);
5174         }
5175         var d = ++key;
5176         cs[0]._nodup = d;
5177         for(i = 1; c = cs[i]; i++){
5178             if(c._nodup != d){
5179                 c._nodup = d;
5180             }else{
5181                 r = [];
5182                 for(var j = 0; j < i; j++){
5183                     r[++ri] = cs[j];
5184                 }
5185                 for(j = i+1; cj = cs[j]; j++){
5186                     if(cj._nodup != d){
5187                         cj._nodup = d;
5188                         r[++ri] = cj;
5189                     }
5190                 }
5191                 return r;
5192             }
5193         }
5194         return r;
5195     }
5196
5197     function quickDiffIEXml(c1, c2){
5198         var d = ++key;
5199         for(var i = 0, len = c1.length; i < len; i++){
5200             c1[i].setAttribute("_qdiff", d);
5201         }
5202         var r = [];
5203         for(var i = 0, len = c2.length; i < len; i++){
5204             if(c2[i].getAttribute("_qdiff") != d){
5205                 r[r.length] = c2[i];
5206             }
5207         }
5208         for(var i = 0, len = c1.length; i < len; i++){
5209            c1[i].removeAttribute("_qdiff");
5210         }
5211         return r;
5212     }
5213
5214     function quickDiff(c1, c2){
5215         var len1 = c1.length;
5216         if(!len1){
5217             return c2;
5218         }
5219         if(isIE && c1[0].selectSingleNode){
5220             return quickDiffIEXml(c1, c2);
5221         }
5222         var d = ++key;
5223         for(var i = 0; i < len1; i++){
5224             c1[i]._qdiff = d;
5225         }
5226         var r = [];
5227         for(var i = 0, len = c2.length; i < len; i++){
5228             if(c2[i]._qdiff != d){
5229                 r[r.length] = c2[i];
5230             }
5231         }
5232         return r;
5233     }
5234
5235     function quickId(ns, mode, root, id){
5236         if(ns == root){
5237            var d = root.ownerDocument || root;
5238            return d.getElementById(id);
5239         }
5240         ns = getNodes(ns, mode, "*");
5241         return byId(ns, null, id);
5242     }
5243
5244     return {
5245         getStyle : function(el, name){
5246             return Roo.fly(el).getStyle(name);
5247         },
5248         /**
5249          * Compiles a selector/xpath query into a reusable function. The returned function
5250          * takes one parameter "root" (optional), which is the context node from where the query should start.
5251          * @param {String} selector The selector/xpath query
5252          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5253          * @return {Function}
5254          */
5255         compile : function(path, type){
5256             type = type || "select";
5257             
5258             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5259             var q = path, mode, lq;
5260             var tk = Roo.DomQuery.matchers;
5261             var tklen = tk.length;
5262             var mm;
5263
5264             // accept leading mode switch
5265             var lmode = q.match(modeRe);
5266             if(lmode && lmode[1]){
5267                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5268                 q = q.replace(lmode[1], "");
5269             }
5270             // strip leading slashes
5271             while(path.substr(0, 1)=="/"){
5272                 path = path.substr(1);
5273             }
5274
5275             while(q && lq != q){
5276                 lq = q;
5277                 var tm = q.match(tagTokenRe);
5278                 if(type == "select"){
5279                     if(tm){
5280                         if(tm[1] == "#"){
5281                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5282                         }else{
5283                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5284                         }
5285                         q = q.replace(tm[0], "");
5286                     }else if(q.substr(0, 1) != '@'){
5287                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5288                     }
5289                 }else{
5290                     if(tm){
5291                         if(tm[1] == "#"){
5292                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5293                         }else{
5294                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5295                         }
5296                         q = q.replace(tm[0], "");
5297                     }
5298                 }
5299                 while(!(mm = q.match(modeRe))){
5300                     var matched = false;
5301                     for(var j = 0; j < tklen; j++){
5302                         var t = tk[j];
5303                         var m = q.match(t.re);
5304                         if(m){
5305                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5306                                                     return m[i];
5307                                                 });
5308                             q = q.replace(m[0], "");
5309                             matched = true;
5310                             break;
5311                         }
5312                     }
5313                     // prevent infinite loop on bad selector
5314                     if(!matched){
5315                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5316                     }
5317                 }
5318                 if(mm[1]){
5319                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5320                     q = q.replace(mm[1], "");
5321                 }
5322             }
5323             fn[fn.length] = "return nodup(n);\n}";
5324             
5325              /** 
5326               * list of variables that need from compression as they are used by eval.
5327              *  eval:var:batch 
5328              *  eval:var:nodup
5329              *  eval:var:byTag
5330              *  eval:var:ById
5331              *  eval:var:getNodes
5332              *  eval:var:quickId
5333              *  eval:var:mode
5334              *  eval:var:root
5335              *  eval:var:n
5336              *  eval:var:byClassName
5337              *  eval:var:byPseudo
5338              *  eval:var:byAttribute
5339              *  eval:var:attrValue
5340              * 
5341              **/ 
5342             eval(fn.join(""));
5343             return f;
5344         },
5345
5346         /**
5347          * Selects a group of elements.
5348          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Array}
5351          */
5352         select : function(path, root, type){
5353             if(!root || root == document){
5354                 root = document;
5355             }
5356             if(typeof root == "string"){
5357                 root = document.getElementById(root);
5358             }
5359             var paths = path.split(",");
5360             var results = [];
5361             for(var i = 0, len = paths.length; i < len; i++){
5362                 var p = paths[i].replace(trimRe, "");
5363                 if(!cache[p]){
5364                     cache[p] = Roo.DomQuery.compile(p);
5365                     if(!cache[p]){
5366                         throw p + " is not a valid selector";
5367                     }
5368                 }
5369                 var result = cache[p](root);
5370                 if(result && result != document){
5371                     results = results.concat(result);
5372                 }
5373             }
5374             if(paths.length > 1){
5375                 return nodup(results);
5376             }
5377             return results;
5378         },
5379
5380         /**
5381          * Selects a single element.
5382          * @param {String} selector The selector/xpath query
5383          * @param {Node} root (optional) The start of the query (defaults to document).
5384          * @return {Element}
5385          */
5386         selectNode : function(path, root){
5387             return Roo.DomQuery.select(path, root)[0];
5388         },
5389
5390         /**
5391          * Selects the value of a node, optionally replacing null with the defaultValue.
5392          * @param {String} selector The selector/xpath query
5393          * @param {Node} root (optional) The start of the query (defaults to document).
5394          * @param {String} defaultValue
5395          */
5396         selectValue : function(path, root, defaultValue){
5397             path = path.replace(trimRe, "");
5398             if(!valueCache[path]){
5399                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5400             }
5401             var n = valueCache[path](root);
5402             n = n[0] ? n[0] : n;
5403             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5404             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5405         },
5406
5407         /**
5408          * Selects the value of a node, parsing integers and floats.
5409          * @param {String} selector The selector/xpath query
5410          * @param {Node} root (optional) The start of the query (defaults to document).
5411          * @param {Number} defaultValue
5412          * @return {Number}
5413          */
5414         selectNumber : function(path, root, defaultValue){
5415             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5416             return parseFloat(v);
5417         },
5418
5419         /**
5420          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5421          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5422          * @param {String} selector The simple selector to test
5423          * @return {Boolean}
5424          */
5425         is : function(el, ss){
5426             if(typeof el == "string"){
5427                 el = document.getElementById(el);
5428             }
5429             var isArray = (el instanceof Array);
5430             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5431             return isArray ? (result.length == el.length) : (result.length > 0);
5432         },
5433
5434         /**
5435          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5436          * @param {Array} el An array of elements to filter
5437          * @param {String} selector The simple selector to test
5438          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5439          * the selector instead of the ones that match
5440          * @return {Array}
5441          */
5442         filter : function(els, ss, nonMatches){
5443             ss = ss.replace(trimRe, "");
5444             if(!simpleCache[ss]){
5445                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5446             }
5447             var result = simpleCache[ss](els);
5448             return nonMatches ? quickDiff(result, els) : result;
5449         },
5450
5451         /**
5452          * Collection of matching regular expressions and code snippets.
5453          */
5454         matchers : [{
5455                 re: /^\.([\w-]+)/,
5456                 select: 'n = byClassName(n, null, " {1} ");'
5457             }, {
5458                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5459                 select: 'n = byPseudo(n, "{1}", "{2}");'
5460             },{
5461                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5462                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5463             }, {
5464                 re: /^#([\w-]+)/,
5465                 select: 'n = byId(n, null, "{1}");'
5466             },{
5467                 re: /^@([\w-]+)/,
5468                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5469             }
5470         ],
5471
5472         /**
5473          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5474          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5475          */
5476         operators : {
5477             "=" : function(a, v){
5478                 return a == v;
5479             },
5480             "!=" : function(a, v){
5481                 return a != v;
5482             },
5483             "^=" : function(a, v){
5484                 return a && a.substr(0, v.length) == v;
5485             },
5486             "$=" : function(a, v){
5487                 return a && a.substr(a.length-v.length) == v;
5488             },
5489             "*=" : function(a, v){
5490                 return a && a.indexOf(v) !== -1;
5491             },
5492             "%=" : function(a, v){
5493                 return (a % v) == 0;
5494             },
5495             "|=" : function(a, v){
5496                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5497             },
5498             "~=" : function(a, v){
5499                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5500             }
5501         },
5502
5503         /**
5504          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5505          * and the argument (if any) supplied in the selector.
5506          */
5507         pseudos : {
5508             "first-child" : function(c){
5509                 var r = [], ri = -1, n;
5510                 for(var i = 0, ci; ci = n = c[i]; i++){
5511                     while((n = n.previousSibling) && n.nodeType != 1);
5512                     if(!n){
5513                         r[++ri] = ci;
5514                     }
5515                 }
5516                 return r;
5517             },
5518
5519             "last-child" : function(c){
5520                 var r = [], ri = -1, n;
5521                 for(var i = 0, ci; ci = n = c[i]; i++){
5522                     while((n = n.nextSibling) && n.nodeType != 1);
5523                     if(!n){
5524                         r[++ri] = ci;
5525                     }
5526                 }
5527                 return r;
5528             },
5529
5530             "nth-child" : function(c, a) {
5531                 var r = [], ri = -1;
5532                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5533                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5534                 for(var i = 0, n; n = c[i]; i++){
5535                     var pn = n.parentNode;
5536                     if (batch != pn._batch) {
5537                         var j = 0;
5538                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5539                             if(cn.nodeType == 1){
5540                                cn.nodeIndex = ++j;
5541                             }
5542                         }
5543                         pn._batch = batch;
5544                     }
5545                     if (f == 1) {
5546                         if (l == 0 || n.nodeIndex == l){
5547                             r[++ri] = n;
5548                         }
5549                     } else if ((n.nodeIndex + l) % f == 0){
5550                         r[++ri] = n;
5551                     }
5552                 }
5553
5554                 return r;
5555             },
5556
5557             "only-child" : function(c){
5558                 var r = [], ri = -1;;
5559                 for(var i = 0, ci; ci = c[i]; i++){
5560                     if(!prev(ci) && !next(ci)){
5561                         r[++ri] = ci;
5562                     }
5563                 }
5564                 return r;
5565             },
5566
5567             "empty" : function(c){
5568                 var r = [], ri = -1;
5569                 for(var i = 0, ci; ci = c[i]; i++){
5570                     var cns = ci.childNodes, j = 0, cn, empty = true;
5571                     while(cn = cns[j]){
5572                         ++j;
5573                         if(cn.nodeType == 1 || cn.nodeType == 3){
5574                             empty = false;
5575                             break;
5576                         }
5577                     }
5578                     if(empty){
5579                         r[++ri] = ci;
5580                     }
5581                 }
5582                 return r;
5583             },
5584
5585             "contains" : function(c, v){
5586                 var r = [], ri = -1;
5587                 for(var i = 0, ci; ci = c[i]; i++){
5588                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5589                         r[++ri] = ci;
5590                     }
5591                 }
5592                 return r;
5593             },
5594
5595             "nodeValue" : function(c, v){
5596                 var r = [], ri = -1;
5597                 for(var i = 0, ci; ci = c[i]; i++){
5598                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5599                         r[++ri] = ci;
5600                     }
5601                 }
5602                 return r;
5603             },
5604
5605             "checked" : function(c){
5606                 var r = [], ri = -1;
5607                 for(var i = 0, ci; ci = c[i]; i++){
5608                     if(ci.checked == true){
5609                         r[++ri] = ci;
5610                     }
5611                 }
5612                 return r;
5613             },
5614
5615             "not" : function(c, ss){
5616                 return Roo.DomQuery.filter(c, ss, true);
5617             },
5618
5619             "odd" : function(c){
5620                 return this["nth-child"](c, "odd");
5621             },
5622
5623             "even" : function(c){
5624                 return this["nth-child"](c, "even");
5625             },
5626
5627             "nth" : function(c, a){
5628                 return c[a-1] || [];
5629             },
5630
5631             "first" : function(c){
5632                 return c[0] || [];
5633             },
5634
5635             "last" : function(c){
5636                 return c[c.length-1] || [];
5637             },
5638
5639             "has" : function(c, ss){
5640                 var s = Roo.DomQuery.select;
5641                 var r = [], ri = -1;
5642                 for(var i = 0, ci; ci = c[i]; i++){
5643                     if(s(ss, ci).length > 0){
5644                         r[++ri] = ci;
5645                     }
5646                 }
5647                 return r;
5648             },
5649
5650             "next" : function(c, ss){
5651                 var is = Roo.DomQuery.is;
5652                 var r = [], ri = -1;
5653                 for(var i = 0, ci; ci = c[i]; i++){
5654                     var n = next(ci);
5655                     if(n && is(n, ss)){
5656                         r[++ri] = ci;
5657                     }
5658                 }
5659                 return r;
5660             },
5661
5662             "prev" : function(c, ss){
5663                 var is = Roo.DomQuery.is;
5664                 var r = [], ri = -1;
5665                 for(var i = 0, ci; ci = c[i]; i++){
5666                     var n = prev(ci);
5667                     if(n && is(n, ss)){
5668                         r[++ri] = ci;
5669                     }
5670                 }
5671                 return r;
5672             }
5673         }
5674     };
5675 }();
5676
5677 /**
5678  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5679  * @param {String} path The selector/xpath query
5680  * @param {Node} root (optional) The start of the query (defaults to document).
5681  * @return {Array}
5682  * @member Roo
5683  * @method query
5684  */
5685 Roo.query = Roo.DomQuery.select;
5686 /*
5687  * Based on:
5688  * Ext JS Library 1.1.1
5689  * Copyright(c) 2006-2007, Ext JS, LLC.
5690  *
5691  * Originally Released Under LGPL - original licence link has changed is not relivant.
5692  *
5693  * Fork - LGPL
5694  * <script type="text/javascript">
5695  */
5696
5697 /**
5698  * @class Roo.util.Observable
5699  * Base class that provides a common interface for publishing events. Subclasses are expected to
5700  * to have a property "events" with all the events defined.<br>
5701  * For example:
5702  * <pre><code>
5703  Employee = function(name){
5704     this.name = name;
5705     this.addEvents({
5706         "fired" : true,
5707         "quit" : true
5708     });
5709  }
5710  Roo.extend(Employee, Roo.util.Observable);
5711 </code></pre>
5712  * @param {Object} config properties to use (incuding events / listeners)
5713  */
5714
5715 Roo.util.Observable = function(cfg){
5716     
5717     cfg = cfg|| {};
5718     this.addEvents(cfg.events || {});
5719     if (cfg.events) {
5720         delete cfg.events; // make sure
5721     }
5722      
5723     Roo.apply(this, cfg);
5724     
5725     if(this.listeners){
5726         this.on(this.listeners);
5727         delete this.listeners;
5728     }
5729 };
5730 Roo.util.Observable.prototype = {
5731     /** 
5732  * @cfg {Object} listeners  list of events and functions to call for this object, 
5733  * For example :
5734  * <pre><code>
5735     listeners :  { 
5736        'click' : function(e) {
5737            ..... 
5738         } ,
5739         .... 
5740     } 
5741   </code></pre>
5742  */
5743     
5744     
5745     /**
5746      * Fires the specified event with the passed parameters (minus the event name).
5747      * @param {String} eventName
5748      * @param {Object...} args Variable number of parameters are passed to handlers
5749      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5750      */
5751     fireEvent : function(){
5752         var ce = this.events[arguments[0].toLowerCase()];
5753         if(typeof ce == "object"){
5754             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5755         }else{
5756             return true;
5757         }
5758     },
5759
5760     // private
5761     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5762
5763     /**
5764      * Appends an event handler to this component
5765      * @param {String}   eventName The type of event to listen for
5766      * @param {Function} handler The method the event invokes
5767      * @param {Object}   scope (optional) The scope in which to execute the handler
5768      * function. The handler function's "this" context.
5769      * @param {Object}   options (optional) An object containing handler configuration
5770      * properties. This may contain any of the following properties:<ul>
5771      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5772      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5773      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5774      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5775      * by the specified number of milliseconds. If the event fires again within that time, the original
5776      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5777      * </ul><br>
5778      * <p>
5779      * <b>Combining Options</b><br>
5780      * Using the options argument, it is possible to combine different types of listeners:<br>
5781      * <br>
5782      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5783                 <pre><code>
5784                 el.on('click', this.onClick, this, {
5785                         single: true,
5786                 delay: 100,
5787                 forumId: 4
5788                 });
5789                 </code></pre>
5790      * <p>
5791      * <b>Attaching multiple handlers in 1 call</b><br>
5792      * The method also allows for a single argument to be passed which is a config object containing properties
5793      * which specify multiple handlers.
5794      * <pre><code>
5795                 el.on({
5796                         'click': {
5797                         fn: this.onClick,
5798                         scope: this,
5799                         delay: 100
5800                 }, 
5801                 'mouseover': {
5802                         fn: this.onMouseOver,
5803                         scope: this
5804                 },
5805                 'mouseout': {
5806                         fn: this.onMouseOut,
5807                         scope: this
5808                 }
5809                 });
5810                 </code></pre>
5811      * <p>
5812      * Or a shorthand syntax which passes the same scope object to all handlers:
5813         <pre><code>
5814                 el.on({
5815                         'click': this.onClick,
5816                 'mouseover': this.onMouseOver,
5817                 'mouseout': this.onMouseOut,
5818                 scope: this
5819                 });
5820                 </code></pre>
5821      */
5822     addListener : function(eventName, fn, scope, o){
5823         if(typeof eventName == "object"){
5824             o = eventName;
5825             for(var e in o){
5826                 if(this.filterOptRe.test(e)){
5827                     continue;
5828                 }
5829                 if(typeof o[e] == "function"){
5830                     // shared options
5831                     this.addListener(e, o[e], o.scope,  o);
5832                 }else{
5833                     // individual options
5834                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5835                 }
5836             }
5837             return;
5838         }
5839         o = (!o || typeof o == "boolean") ? {} : o;
5840         eventName = eventName.toLowerCase();
5841         var ce = this.events[eventName] || true;
5842         if(typeof ce == "boolean"){
5843             ce = new Roo.util.Event(this, eventName);
5844             this.events[eventName] = ce;
5845         }
5846         ce.addListener(fn, scope, o);
5847     },
5848
5849     /**
5850      * Removes a listener
5851      * @param {String}   eventName     The type of event to listen for
5852      * @param {Function} handler        The handler to remove
5853      * @param {Object}   scope  (optional) The scope (this object) for the handler
5854      */
5855     removeListener : function(eventName, fn, scope){
5856         var ce = this.events[eventName.toLowerCase()];
5857         if(typeof ce == "object"){
5858             ce.removeListener(fn, scope);
5859         }
5860     },
5861
5862     /**
5863      * Removes all listeners for this object
5864      */
5865     purgeListeners : function(){
5866         for(var evt in this.events){
5867             if(typeof this.events[evt] == "object"){
5868                  this.events[evt].clearListeners();
5869             }
5870         }
5871     },
5872
5873     relayEvents : function(o, events){
5874         var createHandler = function(ename){
5875             return function(){
5876                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5877             };
5878         };
5879         for(var i = 0, len = events.length; i < len; i++){
5880             var ename = events[i];
5881             if(!this.events[ename]){ this.events[ename] = true; };
5882             o.on(ename, createHandler(ename), this);
5883         }
5884     },
5885
5886     /**
5887      * Used to define events on this Observable
5888      * @param {Object} object The object with the events defined
5889      */
5890     addEvents : function(o){
5891         if(!this.events){
5892             this.events = {};
5893         }
5894         Roo.applyIf(this.events, o);
5895     },
5896
5897     /**
5898      * Checks to see if this object has any listeners for a specified event
5899      * @param {String} eventName The name of the event to check for
5900      * @return {Boolean} True if the event is being listened for, else false
5901      */
5902     hasListener : function(eventName){
5903         var e = this.events[eventName];
5904         return typeof e == "object" && e.listeners.length > 0;
5905     }
5906 };
5907 /**
5908  * Appends an event handler to this element (shorthand for addListener)
5909  * @param {String}   eventName     The type of event to listen for
5910  * @param {Function} handler        The method the event invokes
5911  * @param {Object}   scope (optional) The scope in which to execute the handler
5912  * function. The handler function's "this" context.
5913  * @param {Object}   options  (optional)
5914  * @method
5915  */
5916 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5917 /**
5918  * Removes a listener (shorthand for removeListener)
5919  * @param {String}   eventName     The type of event to listen for
5920  * @param {Function} handler        The handler to remove
5921  * @param {Object}   scope  (optional) The scope (this object) for the handler
5922  * @method
5923  */
5924 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5925
5926 /**
5927  * Starts capture on the specified Observable. All events will be passed
5928  * to the supplied function with the event name + standard signature of the event
5929  * <b>before</b> the event is fired. If the supplied function returns false,
5930  * the event will not fire.
5931  * @param {Observable} o The Observable to capture
5932  * @param {Function} fn The function to call
5933  * @param {Object} scope (optional) The scope (this object) for the fn
5934  * @static
5935  */
5936 Roo.util.Observable.capture = function(o, fn, scope){
5937     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5938 };
5939
5940 /**
5941  * Removes <b>all</b> added captures from the Observable.
5942  * @param {Observable} o The Observable to release
5943  * @static
5944  */
5945 Roo.util.Observable.releaseCapture = function(o){
5946     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5947 };
5948
5949 (function(){
5950
5951     var createBuffered = function(h, o, scope){
5952         var task = new Roo.util.DelayedTask();
5953         return function(){
5954             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5955         };
5956     };
5957
5958     var createSingle = function(h, e, fn, scope){
5959         return function(){
5960             e.removeListener(fn, scope);
5961             return h.apply(scope, arguments);
5962         };
5963     };
5964
5965     var createDelayed = function(h, o, scope){
5966         return function(){
5967             var args = Array.prototype.slice.call(arguments, 0);
5968             setTimeout(function(){
5969                 h.apply(scope, args);
5970             }, o.delay || 10);
5971         };
5972     };
5973
5974     Roo.util.Event = function(obj, name){
5975         this.name = name;
5976         this.obj = obj;
5977         this.listeners = [];
5978     };
5979
5980     Roo.util.Event.prototype = {
5981         addListener : function(fn, scope, options){
5982             var o = options || {};
5983             scope = scope || this.obj;
5984             if(!this.isListening(fn, scope)){
5985                 var l = {fn: fn, scope: scope, options: o};
5986                 var h = fn;
5987                 if(o.delay){
5988                     h = createDelayed(h, o, scope);
5989                 }
5990                 if(o.single){
5991                     h = createSingle(h, this, fn, scope);
5992                 }
5993                 if(o.buffer){
5994                     h = createBuffered(h, o, scope);
5995                 }
5996                 l.fireFn = h;
5997                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5998                     this.listeners.push(l);
5999                 }else{
6000                     this.listeners = this.listeners.slice(0);
6001                     this.listeners.push(l);
6002                 }
6003             }
6004         },
6005
6006         findListener : function(fn, scope){
6007             scope = scope || this.obj;
6008             var ls = this.listeners;
6009             for(var i = 0, len = ls.length; i < len; i++){
6010                 var l = ls[i];
6011                 if(l.fn == fn && l.scope == scope){
6012                     return i;
6013                 }
6014             }
6015             return -1;
6016         },
6017
6018         isListening : function(fn, scope){
6019             return this.findListener(fn, scope) != -1;
6020         },
6021
6022         removeListener : function(fn, scope){
6023             var index;
6024             if((index = this.findListener(fn, scope)) != -1){
6025                 if(!this.firing){
6026                     this.listeners.splice(index, 1);
6027                 }else{
6028                     this.listeners = this.listeners.slice(0);
6029                     this.listeners.splice(index, 1);
6030                 }
6031                 return true;
6032             }
6033             return false;
6034         },
6035
6036         clearListeners : function(){
6037             this.listeners = [];
6038         },
6039
6040         fire : function(){
6041             var ls = this.listeners, scope, len = ls.length;
6042             if(len > 0){
6043                 this.firing = true;
6044                 var args = Array.prototype.slice.call(arguments, 0);
6045                 for(var i = 0; i < len; i++){
6046                     var l = ls[i];
6047                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6048                         this.firing = false;
6049                         return false;
6050                     }
6051                 }
6052                 this.firing = false;
6053             }
6054             return true;
6055         }
6056     };
6057 })();/*
6058  * RooJS Library 
6059  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6060  *
6061  * Licence LGPL 
6062  *
6063  */
6064  
6065 /**
6066  * @class Roo.Document
6067  * @extends Roo.util.Observable
6068  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6069  * 
6070  * @param {Object} config the methods and properties of the 'base' class for the application.
6071  * 
6072  *  Generic Page handler - implement this to start your app..
6073  * 
6074  * eg.
6075  *  MyProject = new Roo.Document({
6076         events : {
6077             'load' : true // your events..
6078         },
6079         listeners : {
6080             'ready' : function() {
6081                 // fired on Roo.onReady()
6082             }
6083         }
6084  * 
6085  */
6086 Roo.Document = function(cfg) {
6087      
6088     this.addEvents({ 
6089         'ready' : true
6090     });
6091     Roo.util.Observable.call(this,cfg);
6092     
6093     var _this = this;
6094     
6095     Roo.onReady(function() {
6096         _this.fireEvent('ready');
6097     },null,false);
6098     
6099     
6100 }
6101
6102 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6103  * Based on:
6104  * Ext JS Library 1.1.1
6105  * Copyright(c) 2006-2007, Ext JS, LLC.
6106  *
6107  * Originally Released Under LGPL - original licence link has changed is not relivant.
6108  *
6109  * Fork - LGPL
6110  * <script type="text/javascript">
6111  */
6112
6113 /**
6114  * @class Roo.EventManager
6115  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6116  * several useful events directly.
6117  * See {@link Roo.EventObject} for more details on normalized event objects.
6118  * @singleton
6119  */
6120 Roo.EventManager = function(){
6121     var docReadyEvent, docReadyProcId, docReadyState = false;
6122     var resizeEvent, resizeTask, textEvent, textSize;
6123     var E = Roo.lib.Event;
6124     var D = Roo.lib.Dom;
6125
6126     
6127     
6128
6129     var fireDocReady = function(){
6130         if(!docReadyState){
6131             docReadyState = true;
6132             Roo.isReady = true;
6133             if(docReadyProcId){
6134                 clearInterval(docReadyProcId);
6135             }
6136             if(Roo.isGecko || Roo.isOpera) {
6137                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6138             }
6139             if(Roo.isIE){
6140                 var defer = document.getElementById("ie-deferred-loader");
6141                 if(defer){
6142                     defer.onreadystatechange = null;
6143                     defer.parentNode.removeChild(defer);
6144                 }
6145             }
6146             if(docReadyEvent){
6147                 docReadyEvent.fire();
6148                 docReadyEvent.clearListeners();
6149             }
6150         }
6151     };
6152     
6153     var initDocReady = function(){
6154         docReadyEvent = new Roo.util.Event();
6155         if(Roo.isGecko || Roo.isOpera) {
6156             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6157         }else if(Roo.isIE){
6158             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6159             var defer = document.getElementById("ie-deferred-loader");
6160             defer.onreadystatechange = function(){
6161                 if(this.readyState == "complete"){
6162                     fireDocReady();
6163                 }
6164             };
6165         }else if(Roo.isSafari){ 
6166             docReadyProcId = setInterval(function(){
6167                 var rs = document.readyState;
6168                 if(rs == "complete") {
6169                     fireDocReady();     
6170                  }
6171             }, 10);
6172         }
6173         // no matter what, make sure it fires on load
6174         E.on(window, "load", fireDocReady);
6175     };
6176
6177     var createBuffered = function(h, o){
6178         var task = new Roo.util.DelayedTask(h);
6179         return function(e){
6180             // create new event object impl so new events don't wipe out properties
6181             e = new Roo.EventObjectImpl(e);
6182             task.delay(o.buffer, h, null, [e]);
6183         };
6184     };
6185
6186     var createSingle = function(h, el, ename, fn){
6187         return function(e){
6188             Roo.EventManager.removeListener(el, ename, fn);
6189             h(e);
6190         };
6191     };
6192
6193     var createDelayed = function(h, o){
6194         return function(e){
6195             // create new event object impl so new events don't wipe out properties
6196             e = new Roo.EventObjectImpl(e);
6197             setTimeout(function(){
6198                 h(e);
6199             }, o.delay || 10);
6200         };
6201     };
6202     var transitionEndVal = false;
6203     
6204     var transitionEnd = function()
6205     {
6206         if (transitionEndVal) {
6207             return transitionEndVal;
6208         }
6209         var el = document.createElement('div');
6210
6211         var transEndEventNames = {
6212             WebkitTransition : 'webkitTransitionEnd',
6213             MozTransition    : 'transitionend',
6214             OTransition      : 'oTransitionEnd otransitionend',
6215             transition       : 'transitionend'
6216         };
6217     
6218         for (var name in transEndEventNames) {
6219             if (el.style[name] !== undefined) {
6220                 transitionEndVal = transEndEventNames[name];
6221                 return  transitionEndVal ;
6222             }
6223         }
6224     }
6225     
6226
6227     var listen = function(element, ename, opt, fn, scope){
6228         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6229         fn = fn || o.fn; scope = scope || o.scope;
6230         var el = Roo.getDom(element);
6231         
6232         
6233         if(!el){
6234             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6235         }
6236         
6237         if (ename == 'transitionend') {
6238             ename = transitionEnd();
6239         }
6240         var h = function(e){
6241             e = Roo.EventObject.setEvent(e);
6242             var t;
6243             if(o.delegate){
6244                 t = e.getTarget(o.delegate, el);
6245                 if(!t){
6246                     return;
6247                 }
6248             }else{
6249                 t = e.target;
6250             }
6251             if(o.stopEvent === true){
6252                 e.stopEvent();
6253             }
6254             if(o.preventDefault === true){
6255                e.preventDefault();
6256             }
6257             if(o.stopPropagation === true){
6258                 e.stopPropagation();
6259             }
6260
6261             if(o.normalized === false){
6262                 e = e.browserEvent;
6263             }
6264
6265             fn.call(scope || el, e, t, o);
6266         };
6267         if(o.delay){
6268             h = createDelayed(h, o);
6269         }
6270         if(o.single){
6271             h = createSingle(h, el, ename, fn);
6272         }
6273         if(o.buffer){
6274             h = createBuffered(h, o);
6275         }
6276         fn._handlers = fn._handlers || [];
6277         
6278         
6279         fn._handlers.push([Roo.id(el), ename, h]);
6280         
6281         
6282          
6283         E.on(el, ename, h);
6284         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6285             el.addEventListener("DOMMouseScroll", h, false);
6286             E.on(window, 'unload', function(){
6287                 el.removeEventListener("DOMMouseScroll", h, false);
6288             });
6289         }
6290         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6291             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6292         }
6293         return h;
6294     };
6295
6296     var stopListening = function(el, ename, fn){
6297         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6298         if(hds){
6299             for(var i = 0, len = hds.length; i < len; i++){
6300                 var h = hds[i];
6301                 if(h[0] == id && h[1] == ename){
6302                     hd = h[2];
6303                     hds.splice(i, 1);
6304                     break;
6305                 }
6306             }
6307         }
6308         E.un(el, ename, hd);
6309         el = Roo.getDom(el);
6310         if(ename == "mousewheel" && el.addEventListener){
6311             el.removeEventListener("DOMMouseScroll", hd, false);
6312         }
6313         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6314             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6315         }
6316     };
6317
6318     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6319     
6320     var pub = {
6321         
6322         
6323         /** 
6324          * Fix for doc tools
6325          * @scope Roo.EventManager
6326          */
6327         
6328         
6329         /** 
6330          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6331          * object with a Roo.EventObject
6332          * @param {Function} fn        The method the event invokes
6333          * @param {Object}   scope    An object that becomes the scope of the handler
6334          * @param {boolean}  override If true, the obj passed in becomes
6335          *                             the execution scope of the listener
6336          * @return {Function} The wrapped function
6337          * @deprecated
6338          */
6339         wrap : function(fn, scope, override){
6340             return function(e){
6341                 Roo.EventObject.setEvent(e);
6342                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6343             };
6344         },
6345         
6346         /**
6347      * Appends an event handler to an element (shorthand for addListener)
6348      * @param {String/HTMLElement}   element        The html element or id to assign the
6349      * @param {String}   eventName The type of event to listen for
6350      * @param {Function} handler The method the event invokes
6351      * @param {Object}   scope (optional) The scope in which to execute the handler
6352      * function. The handler function's "this" context.
6353      * @param {Object}   options (optional) An object containing handler configuration
6354      * properties. This may contain any of the following properties:<ul>
6355      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6356      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6357      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6358      * <li>preventDefault {Boolean} True to prevent the default action</li>
6359      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6360      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6361      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6362      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6363      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6364      * by the specified number of milliseconds. If the event fires again within that time, the original
6365      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6366      * </ul><br>
6367      * <p>
6368      * <b>Combining Options</b><br>
6369      * Using the options argument, it is possible to combine different types of listeners:<br>
6370      * <br>
6371      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6372      * Code:<pre><code>
6373 el.on('click', this.onClick, this, {
6374     single: true,
6375     delay: 100,
6376     stopEvent : true,
6377     forumId: 4
6378 });</code></pre>
6379      * <p>
6380      * <b>Attaching multiple handlers in 1 call</b><br>
6381       * The method also allows for a single argument to be passed which is a config object containing properties
6382      * which specify multiple handlers.
6383      * <p>
6384      * Code:<pre><code>
6385 el.on({
6386     'click' : {
6387         fn: this.onClick
6388         scope: this,
6389         delay: 100
6390     },
6391     'mouseover' : {
6392         fn: this.onMouseOver
6393         scope: this
6394     },
6395     'mouseout' : {
6396         fn: this.onMouseOut
6397         scope: this
6398     }
6399 });</code></pre>
6400      * <p>
6401      * Or a shorthand syntax:<br>
6402      * Code:<pre><code>
6403 el.on({
6404     'click' : this.onClick,
6405     'mouseover' : this.onMouseOver,
6406     'mouseout' : this.onMouseOut
6407     scope: this
6408 });</code></pre>
6409      */
6410         addListener : function(element, eventName, fn, scope, options){
6411             if(typeof eventName == "object"){
6412                 var o = eventName;
6413                 for(var e in o){
6414                     if(propRe.test(e)){
6415                         continue;
6416                     }
6417                     if(typeof o[e] == "function"){
6418                         // shared options
6419                         listen(element, e, o, o[e], o.scope);
6420                     }else{
6421                         // individual options
6422                         listen(element, e, o[e]);
6423                     }
6424                 }
6425                 return;
6426             }
6427             return listen(element, eventName, options, fn, scope);
6428         },
6429         
6430         /**
6431          * Removes an event handler
6432          *
6433          * @param {String/HTMLElement}   element        The id or html element to remove the 
6434          *                             event from
6435          * @param {String}   eventName     The type of event
6436          * @param {Function} fn
6437          * @return {Boolean} True if a listener was actually removed
6438          */
6439         removeListener : function(element, eventName, fn){
6440             return stopListening(element, eventName, fn);
6441         },
6442         
6443         /**
6444          * Fires when the document is ready (before onload and before images are loaded). Can be 
6445          * accessed shorthanded Roo.onReady().
6446          * @param {Function} fn        The method the event invokes
6447          * @param {Object}   scope    An  object that becomes the scope of the handler
6448          * @param {boolean}  options
6449          */
6450         onDocumentReady : function(fn, scope, options){
6451             if(docReadyState){ // if it already fired
6452                 docReadyEvent.addListener(fn, scope, options);
6453                 docReadyEvent.fire();
6454                 docReadyEvent.clearListeners();
6455                 return;
6456             }
6457             if(!docReadyEvent){
6458                 initDocReady();
6459             }
6460             docReadyEvent.addListener(fn, scope, options);
6461         },
6462         
6463         /**
6464          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6465          * @param {Function} fn        The method the event invokes
6466          * @param {Object}   scope    An object that becomes the scope of the handler
6467          * @param {boolean}  options
6468          */
6469         onWindowResize : function(fn, scope, options){
6470             if(!resizeEvent){
6471                 resizeEvent = new Roo.util.Event();
6472                 resizeTask = new Roo.util.DelayedTask(function(){
6473                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6474                 });
6475                 E.on(window, "resize", function(){
6476                     if(Roo.isIE){
6477                         resizeTask.delay(50);
6478                     }else{
6479                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6480                     }
6481                 });
6482             }
6483             resizeEvent.addListener(fn, scope, options);
6484         },
6485
6486         /**
6487          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6488          * @param {Function} fn        The method the event invokes
6489          * @param {Object}   scope    An object that becomes the scope of the handler
6490          * @param {boolean}  options
6491          */
6492         onTextResize : function(fn, scope, options){
6493             if(!textEvent){
6494                 textEvent = new Roo.util.Event();
6495                 var textEl = new Roo.Element(document.createElement('div'));
6496                 textEl.dom.className = 'x-text-resize';
6497                 textEl.dom.innerHTML = 'X';
6498                 textEl.appendTo(document.body);
6499                 textSize = textEl.dom.offsetHeight;
6500                 setInterval(function(){
6501                     if(textEl.dom.offsetHeight != textSize){
6502                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6503                     }
6504                 }, this.textResizeInterval);
6505             }
6506             textEvent.addListener(fn, scope, options);
6507         },
6508
6509         /**
6510          * Removes the passed window resize listener.
6511          * @param {Function} fn        The method the event invokes
6512          * @param {Object}   scope    The scope of handler
6513          */
6514         removeResizeListener : function(fn, scope){
6515             if(resizeEvent){
6516                 resizeEvent.removeListener(fn, scope);
6517             }
6518         },
6519
6520         // private
6521         fireResize : function(){
6522             if(resizeEvent){
6523                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6524             }   
6525         },
6526         /**
6527          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6528          */
6529         ieDeferSrc : false,
6530         /**
6531          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6532          */
6533         textResizeInterval : 50
6534     };
6535     
6536     /**
6537      * Fix for doc tools
6538      * @scopeAlias pub=Roo.EventManager
6539      */
6540     
6541      /**
6542      * Appends an event handler to an element (shorthand for addListener)
6543      * @param {String/HTMLElement}   element        The html element or id to assign the
6544      * @param {String}   eventName The type of event to listen for
6545      * @param {Function} handler The method the event invokes
6546      * @param {Object}   scope (optional) The scope in which to execute the handler
6547      * function. The handler function's "this" context.
6548      * @param {Object}   options (optional) An object containing handler configuration
6549      * properties. This may contain any of the following properties:<ul>
6550      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6551      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6552      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6553      * <li>preventDefault {Boolean} True to prevent the default action</li>
6554      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6555      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6556      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6557      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6558      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6559      * by the specified number of milliseconds. If the event fires again within that time, the original
6560      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6561      * </ul><br>
6562      * <p>
6563      * <b>Combining Options</b><br>
6564      * Using the options argument, it is possible to combine different types of listeners:<br>
6565      * <br>
6566      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6567      * Code:<pre><code>
6568 el.on('click', this.onClick, this, {
6569     single: true,
6570     delay: 100,
6571     stopEvent : true,
6572     forumId: 4
6573 });</code></pre>
6574      * <p>
6575      * <b>Attaching multiple handlers in 1 call</b><br>
6576       * The method also allows for a single argument to be passed which is a config object containing properties
6577      * which specify multiple handlers.
6578      * <p>
6579      * Code:<pre><code>
6580 el.on({
6581     'click' : {
6582         fn: this.onClick
6583         scope: this,
6584         delay: 100
6585     },
6586     'mouseover' : {
6587         fn: this.onMouseOver
6588         scope: this
6589     },
6590     'mouseout' : {
6591         fn: this.onMouseOut
6592         scope: this
6593     }
6594 });</code></pre>
6595      * <p>
6596      * Or a shorthand syntax:<br>
6597      * Code:<pre><code>
6598 el.on({
6599     'click' : this.onClick,
6600     'mouseover' : this.onMouseOver,
6601     'mouseout' : this.onMouseOut
6602     scope: this
6603 });</code></pre>
6604      */
6605     pub.on = pub.addListener;
6606     pub.un = pub.removeListener;
6607
6608     pub.stoppedMouseDownEvent = new Roo.util.Event();
6609     return pub;
6610 }();
6611 /**
6612   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6613   * @param {Function} fn        The method the event invokes
6614   * @param {Object}   scope    An  object that becomes the scope of the handler
6615   * @param {boolean}  override If true, the obj passed in becomes
6616   *                             the execution scope of the listener
6617   * @member Roo
6618   * @method onReady
6619  */
6620 Roo.onReady = Roo.EventManager.onDocumentReady;
6621
6622 Roo.onReady(function(){
6623     var bd = Roo.get(document.body);
6624     if(!bd){ return; }
6625
6626     var cls = [
6627             Roo.isIE ? "roo-ie"
6628             : Roo.isGecko ? "roo-gecko"
6629             : Roo.isOpera ? "roo-opera"
6630             : Roo.isSafari ? "roo-safari" : ""];
6631
6632     if(Roo.isMac){
6633         cls.push("roo-mac");
6634     }
6635     if(Roo.isLinux){
6636         cls.push("roo-linux");
6637     }
6638     if(Roo.isIOS){
6639         cls.push("roo-ios");
6640     }
6641     if(Roo.isTouch){
6642         cls.push("roo-touch");
6643     }
6644     if(Roo.isBorderBox){
6645         cls.push('roo-border-box');
6646     }
6647     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6648         var p = bd.dom.parentNode;
6649         if(p){
6650             p.className += ' roo-strict';
6651         }
6652     }
6653     bd.addClass(cls.join(' '));
6654 });
6655
6656 /**
6657  * @class Roo.EventObject
6658  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6659  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6660  * Example:
6661  * <pre><code>
6662  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6663     e.preventDefault();
6664     var target = e.getTarget();
6665     ...
6666  }
6667  var myDiv = Roo.get("myDiv");
6668  myDiv.on("click", handleClick);
6669  //or
6670  Roo.EventManager.on("myDiv", 'click', handleClick);
6671  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6672  </code></pre>
6673  * @singleton
6674  */
6675 Roo.EventObject = function(){
6676     
6677     var E = Roo.lib.Event;
6678     
6679     // safari keypress events for special keys return bad keycodes
6680     var safariKeys = {
6681         63234 : 37, // left
6682         63235 : 39, // right
6683         63232 : 38, // up
6684         63233 : 40, // down
6685         63276 : 33, // page up
6686         63277 : 34, // page down
6687         63272 : 46, // delete
6688         63273 : 36, // home
6689         63275 : 35  // end
6690     };
6691
6692     // normalize button clicks
6693     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6694                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6695
6696     Roo.EventObjectImpl = function(e){
6697         if(e){
6698             this.setEvent(e.browserEvent || e);
6699         }
6700     };
6701     Roo.EventObjectImpl.prototype = {
6702         /**
6703          * Used to fix doc tools.
6704          * @scope Roo.EventObject.prototype
6705          */
6706             
6707
6708         
6709         
6710         /** The normal browser event */
6711         browserEvent : null,
6712         /** The button pressed in a mouse event */
6713         button : -1,
6714         /** True if the shift key was down during the event */
6715         shiftKey : false,
6716         /** True if the control key was down during the event */
6717         ctrlKey : false,
6718         /** True if the alt key was down during the event */
6719         altKey : false,
6720
6721         /** Key constant 
6722         * @type Number */
6723         BACKSPACE : 8,
6724         /** Key constant 
6725         * @type Number */
6726         TAB : 9,
6727         /** Key constant 
6728         * @type Number */
6729         RETURN : 13,
6730         /** Key constant 
6731         * @type Number */
6732         ENTER : 13,
6733         /** Key constant 
6734         * @type Number */
6735         SHIFT : 16,
6736         /** Key constant 
6737         * @type Number */
6738         CONTROL : 17,
6739         /** Key constant 
6740         * @type Number */
6741         ESC : 27,
6742         /** Key constant 
6743         * @type Number */
6744         SPACE : 32,
6745         /** Key constant 
6746         * @type Number */
6747         PAGEUP : 33,
6748         /** Key constant 
6749         * @type Number */
6750         PAGEDOWN : 34,
6751         /** Key constant 
6752         * @type Number */
6753         END : 35,
6754         /** Key constant 
6755         * @type Number */
6756         HOME : 36,
6757         /** Key constant 
6758         * @type Number */
6759         LEFT : 37,
6760         /** Key constant 
6761         * @type Number */
6762         UP : 38,
6763         /** Key constant 
6764         * @type Number */
6765         RIGHT : 39,
6766         /** Key constant 
6767         * @type Number */
6768         DOWN : 40,
6769         /** Key constant 
6770         * @type Number */
6771         MINUS : 45,
6772         /** Key constant 
6773         * @type Number */
6774         DELETE : 46,
6775         /** Key constant 
6776         * @type Number */
6777         F5 : 116,
6778
6779            /** @private */
6780         setEvent : function(e){
6781             if(e == this || (e && e.browserEvent)){ // already wrapped
6782                 return e;
6783             }
6784             this.browserEvent = e;
6785             if(e){
6786                 // normalize buttons
6787                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6788                 if(e.type == 'click' && this.button == -1){
6789                     this.button = 0;
6790                 }
6791                 this.type = e.type;
6792                 this.shiftKey = e.shiftKey;
6793                 // mac metaKey behaves like ctrlKey
6794                 this.ctrlKey = e.ctrlKey || e.metaKey;
6795                 this.altKey = e.altKey;
6796                 // in getKey these will be normalized for the mac
6797                 this.keyCode = e.keyCode;
6798                 // keyup warnings on firefox.
6799                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6800                 // cache the target for the delayed and or buffered events
6801                 this.target = E.getTarget(e);
6802                 // same for XY
6803                 this.xy = E.getXY(e);
6804             }else{
6805                 this.button = -1;
6806                 this.shiftKey = false;
6807                 this.ctrlKey = false;
6808                 this.altKey = false;
6809                 this.keyCode = 0;
6810                 this.charCode =0;
6811                 this.target = null;
6812                 this.xy = [0, 0];
6813             }
6814             return this;
6815         },
6816
6817         /**
6818          * Stop the event (preventDefault and stopPropagation)
6819          */
6820         stopEvent : function(){
6821             if(this.browserEvent){
6822                 if(this.browserEvent.type == 'mousedown'){
6823                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6824                 }
6825                 E.stopEvent(this.browserEvent);
6826             }
6827         },
6828
6829         /**
6830          * Prevents the browsers default handling of the event.
6831          */
6832         preventDefault : function(){
6833             if(this.browserEvent){
6834                 E.preventDefault(this.browserEvent);
6835             }
6836         },
6837
6838         /** @private */
6839         isNavKeyPress : function(){
6840             var k = this.keyCode;
6841             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6842             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6843         },
6844
6845         isSpecialKey : function(){
6846             var k = this.keyCode;
6847             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6848             (k == 16) || (k == 17) ||
6849             (k >= 18 && k <= 20) ||
6850             (k >= 33 && k <= 35) ||
6851             (k >= 36 && k <= 39) ||
6852             (k >= 44 && k <= 45);
6853         },
6854         /**
6855          * Cancels bubbling of the event.
6856          */
6857         stopPropagation : function(){
6858             if(this.browserEvent){
6859                 if(this.type == 'mousedown'){
6860                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6861                 }
6862                 E.stopPropagation(this.browserEvent);
6863             }
6864         },
6865
6866         /**
6867          * Gets the key code for the event.
6868          * @return {Number}
6869          */
6870         getCharCode : function(){
6871             return this.charCode || this.keyCode;
6872         },
6873
6874         /**
6875          * Returns a normalized keyCode for the event.
6876          * @return {Number} The key code
6877          */
6878         getKey : function(){
6879             var k = this.keyCode || this.charCode;
6880             return Roo.isSafari ? (safariKeys[k] || k) : k;
6881         },
6882
6883         /**
6884          * Gets the x coordinate of the event.
6885          * @return {Number}
6886          */
6887         getPageX : function(){
6888             return this.xy[0];
6889         },
6890
6891         /**
6892          * Gets the y coordinate of the event.
6893          * @return {Number}
6894          */
6895         getPageY : function(){
6896             return this.xy[1];
6897         },
6898
6899         /**
6900          * Gets the time of the event.
6901          * @return {Number}
6902          */
6903         getTime : function(){
6904             if(this.browserEvent){
6905                 return E.getTime(this.browserEvent);
6906             }
6907             return null;
6908         },
6909
6910         /**
6911          * Gets the page coordinates of the event.
6912          * @return {Array} The xy values like [x, y]
6913          */
6914         getXY : function(){
6915             return this.xy;
6916         },
6917
6918         /**
6919          * Gets the target for the event.
6920          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6921          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6922                 search as a number or element (defaults to 10 || document.body)
6923          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6924          * @return {HTMLelement}
6925          */
6926         getTarget : function(selector, maxDepth, returnEl){
6927             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6928         },
6929         /**
6930          * Gets the related target.
6931          * @return {HTMLElement}
6932          */
6933         getRelatedTarget : function(){
6934             if(this.browserEvent){
6935                 return E.getRelatedTarget(this.browserEvent);
6936             }
6937             return null;
6938         },
6939
6940         /**
6941          * Normalizes mouse wheel delta across browsers
6942          * @return {Number} The delta
6943          */
6944         getWheelDelta : function(){
6945             var e = this.browserEvent;
6946             var delta = 0;
6947             if(e.wheelDelta){ /* IE/Opera. */
6948                 delta = e.wheelDelta/120;
6949             }else if(e.detail){ /* Mozilla case. */
6950                 delta = -e.detail/3;
6951             }
6952             return delta;
6953         },
6954
6955         /**
6956          * Returns true if the control, meta, shift or alt key was pressed during this event.
6957          * @return {Boolean}
6958          */
6959         hasModifier : function(){
6960             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6961         },
6962
6963         /**
6964          * Returns true if the target of this event equals el or is a child of el
6965          * @param {String/HTMLElement/Element} el
6966          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6967          * @return {Boolean}
6968          */
6969         within : function(el, related){
6970             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6971             return t && Roo.fly(el).contains(t);
6972         },
6973
6974         getPoint : function(){
6975             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6976         }
6977     };
6978
6979     return new Roo.EventObjectImpl();
6980 }();
6981             
6982     /*
6983  * Based on:
6984  * Ext JS Library 1.1.1
6985  * Copyright(c) 2006-2007, Ext JS, LLC.
6986  *
6987  * Originally Released Under LGPL - original licence link has changed is not relivant.
6988  *
6989  * Fork - LGPL
6990  * <script type="text/javascript">
6991  */
6992
6993  
6994 // was in Composite Element!??!?!
6995  
6996 (function(){
6997     var D = Roo.lib.Dom;
6998     var E = Roo.lib.Event;
6999     var A = Roo.lib.Anim;
7000
7001     // local style camelizing for speed
7002     var propCache = {};
7003     var camelRe = /(-[a-z])/gi;
7004     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7005     var view = document.defaultView;
7006
7007 /**
7008  * @class Roo.Element
7009  * Represents an Element in the DOM.<br><br>
7010  * Usage:<br>
7011 <pre><code>
7012 var el = Roo.get("my-div");
7013
7014 // or with getEl
7015 var el = getEl("my-div");
7016
7017 // or with a DOM element
7018 var el = Roo.get(myDivElement);
7019 </code></pre>
7020  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7021  * each call instead of constructing a new one.<br><br>
7022  * <b>Animations</b><br />
7023  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7024  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7025 <pre>
7026 Option    Default   Description
7027 --------- --------  ---------------------------------------------
7028 duration  .35       The duration of the animation in seconds
7029 easing    easeOut   The YUI easing method
7030 callback  none      A function to execute when the anim completes
7031 scope     this      The scope (this) of the callback function
7032 </pre>
7033 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7034 * manipulate the animation. Here's an example:
7035 <pre><code>
7036 var el = Roo.get("my-div");
7037
7038 // no animation
7039 el.setWidth(100);
7040
7041 // default animation
7042 el.setWidth(100, true);
7043
7044 // animation with some options set
7045 el.setWidth(100, {
7046     duration: 1,
7047     callback: this.foo,
7048     scope: this
7049 });
7050
7051 // using the "anim" property to get the Anim object
7052 var opt = {
7053     duration: 1,
7054     callback: this.foo,
7055     scope: this
7056 };
7057 el.setWidth(100, opt);
7058 ...
7059 if(opt.anim.isAnimated()){
7060     opt.anim.stop();
7061 }
7062 </code></pre>
7063 * <b> Composite (Collections of) Elements</b><br />
7064  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7065  * @constructor Create a new Element directly.
7066  * @param {String/HTMLElement} element
7067  * @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).
7068  */
7069     Roo.Element = function(element, forceNew){
7070         var dom = typeof element == "string" ?
7071                 document.getElementById(element) : element;
7072         if(!dom){ // invalid id/element
7073             return null;
7074         }
7075         var id = dom.id;
7076         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7077             return Roo.Element.cache[id];
7078         }
7079
7080         /**
7081          * The DOM element
7082          * @type HTMLElement
7083          */
7084         this.dom = dom;
7085
7086         /**
7087          * The DOM element ID
7088          * @type String
7089          */
7090         this.id = id || Roo.id(dom);
7091     };
7092
7093     var El = Roo.Element;
7094
7095     El.prototype = {
7096         /**
7097          * The element's default display mode  (defaults to "")
7098          * @type String
7099          */
7100         originalDisplay : "",
7101
7102         visibilityMode : 1,
7103         /**
7104          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7105          * @type String
7106          */
7107         defaultUnit : "px",
7108         
7109         /**
7110          * Sets the element's visibility mode. When setVisible() is called it
7111          * will use this to determine whether to set the visibility or the display property.
7112          * @param visMode Element.VISIBILITY or Element.DISPLAY
7113          * @return {Roo.Element} this
7114          */
7115         setVisibilityMode : function(visMode){
7116             this.visibilityMode = visMode;
7117             return this;
7118         },
7119         /**
7120          * Convenience method for setVisibilityMode(Element.DISPLAY)
7121          * @param {String} display (optional) What to set display to when visible
7122          * @return {Roo.Element} this
7123          */
7124         enableDisplayMode : function(display){
7125             this.setVisibilityMode(El.DISPLAY);
7126             if(typeof display != "undefined") { this.originalDisplay = display; }
7127             return this;
7128         },
7129
7130         /**
7131          * 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)
7132          * @param {String} selector The simple selector to test
7133          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7134                 search as a number or element (defaults to 10 || document.body)
7135          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7136          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7137          */
7138         findParent : function(simpleSelector, maxDepth, returnEl){
7139             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7140             maxDepth = maxDepth || 50;
7141             if(typeof maxDepth != "number"){
7142                 stopEl = Roo.getDom(maxDepth);
7143                 maxDepth = 10;
7144             }
7145             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7146                 if(dq.is(p, simpleSelector)){
7147                     return returnEl ? Roo.get(p) : p;
7148                 }
7149                 depth++;
7150                 p = p.parentNode;
7151             }
7152             return null;
7153         },
7154
7155
7156         /**
7157          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7158          * @param {String} selector The simple selector to test
7159          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7160                 search as a number or element (defaults to 10 || document.body)
7161          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7162          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7163          */
7164         findParentNode : function(simpleSelector, maxDepth, returnEl){
7165             var p = Roo.fly(this.dom.parentNode, '_internal');
7166             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7167         },
7168
7169         /**
7170          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7171          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7172          * @param {String} selector The simple selector to test
7173          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7174                 search as a number or element (defaults to 10 || document.body)
7175          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7176          */
7177         up : function(simpleSelector, maxDepth){
7178             return this.findParentNode(simpleSelector, maxDepth, true);
7179         },
7180
7181
7182
7183         /**
7184          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7185          * @param {String} selector The simple selector to test
7186          * @return {Boolean} True if this element matches the selector, else false
7187          */
7188         is : function(simpleSelector){
7189             return Roo.DomQuery.is(this.dom, simpleSelector);
7190         },
7191
7192         /**
7193          * Perform animation on this element.
7194          * @param {Object} args The YUI animation control args
7195          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7196          * @param {Function} onComplete (optional) Function to call when animation completes
7197          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7198          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7199          * @return {Roo.Element} this
7200          */
7201         animate : function(args, duration, onComplete, easing, animType){
7202             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7203             return this;
7204         },
7205
7206         /*
7207          * @private Internal animation call
7208          */
7209         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7210             animType = animType || 'run';
7211             opt = opt || {};
7212             var anim = Roo.lib.Anim[animType](
7213                 this.dom, args,
7214                 (opt.duration || defaultDur) || .35,
7215                 (opt.easing || defaultEase) || 'easeOut',
7216                 function(){
7217                     Roo.callback(cb, this);
7218                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7219                 },
7220                 this
7221             );
7222             opt.anim = anim;
7223             return anim;
7224         },
7225
7226         // private legacy anim prep
7227         preanim : function(a, i){
7228             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7229         },
7230
7231         /**
7232          * Removes worthless text nodes
7233          * @param {Boolean} forceReclean (optional) By default the element
7234          * keeps track if it has been cleaned already so
7235          * you can call this over and over. However, if you update the element and
7236          * need to force a reclean, you can pass true.
7237          */
7238         clean : function(forceReclean){
7239             if(this.isCleaned && forceReclean !== true){
7240                 return this;
7241             }
7242             var ns = /\S/;
7243             var d = this.dom, n = d.firstChild, ni = -1;
7244             while(n){
7245                 var nx = n.nextSibling;
7246                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7247                     d.removeChild(n);
7248                 }else{
7249                     n.nodeIndex = ++ni;
7250                 }
7251                 n = nx;
7252             }
7253             this.isCleaned = true;
7254             return this;
7255         },
7256
7257         // private
7258         calcOffsetsTo : function(el){
7259             el = Roo.get(el);
7260             var d = el.dom;
7261             var restorePos = false;
7262             if(el.getStyle('position') == 'static'){
7263                 el.position('relative');
7264                 restorePos = true;
7265             }
7266             var x = 0, y =0;
7267             var op = this.dom;
7268             while(op && op != d && op.tagName != 'HTML'){
7269                 x+= op.offsetLeft;
7270                 y+= op.offsetTop;
7271                 op = op.offsetParent;
7272             }
7273             if(restorePos){
7274                 el.position('static');
7275             }
7276             return [x, y];
7277         },
7278
7279         /**
7280          * Scrolls this element into view within the passed container.
7281          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7282          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7283          * @return {Roo.Element} this
7284          */
7285         scrollIntoView : function(container, hscroll){
7286             var c = Roo.getDom(container) || document.body;
7287             var el = this.dom;
7288
7289             var o = this.calcOffsetsTo(c),
7290                 l = o[0],
7291                 t = o[1],
7292                 b = t+el.offsetHeight,
7293                 r = l+el.offsetWidth;
7294
7295             var ch = c.clientHeight;
7296             var ct = parseInt(c.scrollTop, 10);
7297             var cl = parseInt(c.scrollLeft, 10);
7298             var cb = ct + ch;
7299             var cr = cl + c.clientWidth;
7300
7301             if(t < ct){
7302                 c.scrollTop = t;
7303             }else if(b > cb){
7304                 c.scrollTop = b-ch;
7305             }
7306
7307             if(hscroll !== false){
7308                 if(l < cl){
7309                     c.scrollLeft = l;
7310                 }else if(r > cr){
7311                     c.scrollLeft = r-c.clientWidth;
7312                 }
7313             }
7314             return this;
7315         },
7316
7317         // private
7318         scrollChildIntoView : function(child, hscroll){
7319             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7320         },
7321
7322         /**
7323          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7324          * the new height may not be available immediately.
7325          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7326          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7327          * @param {Function} onComplete (optional) Function to call when animation completes
7328          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7329          * @return {Roo.Element} this
7330          */
7331         autoHeight : function(animate, duration, onComplete, easing){
7332             var oldHeight = this.getHeight();
7333             this.clip();
7334             this.setHeight(1); // force clipping
7335             setTimeout(function(){
7336                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7337                 if(!animate){
7338                     this.setHeight(height);
7339                     this.unclip();
7340                     if(typeof onComplete == "function"){
7341                         onComplete();
7342                     }
7343                 }else{
7344                     this.setHeight(oldHeight); // restore original height
7345                     this.setHeight(height, animate, duration, function(){
7346                         this.unclip();
7347                         if(typeof onComplete == "function") { onComplete(); }
7348                     }.createDelegate(this), easing);
7349                 }
7350             }.createDelegate(this), 0);
7351             return this;
7352         },
7353
7354         /**
7355          * Returns true if this element is an ancestor of the passed element
7356          * @param {HTMLElement/String} el The element to check
7357          * @return {Boolean} True if this element is an ancestor of el, else false
7358          */
7359         contains : function(el){
7360             if(!el){return false;}
7361             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7362         },
7363
7364         /**
7365          * Checks whether the element is currently visible using both visibility and display properties.
7366          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7367          * @return {Boolean} True if the element is currently visible, else false
7368          */
7369         isVisible : function(deep) {
7370             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7371             if(deep !== true || !vis){
7372                 return vis;
7373             }
7374             var p = this.dom.parentNode;
7375             while(p && p.tagName.toLowerCase() != "body"){
7376                 if(!Roo.fly(p, '_isVisible').isVisible()){
7377                     return false;
7378                 }
7379                 p = p.parentNode;
7380             }
7381             return true;
7382         },
7383
7384         /**
7385          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7386          * @param {String} selector The CSS selector
7387          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7388          * @return {CompositeElement/CompositeElementLite} The composite element
7389          */
7390         select : function(selector, unique){
7391             return El.select(selector, unique, this.dom);
7392         },
7393
7394         /**
7395          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7396          * @param {String} selector The CSS selector
7397          * @return {Array} An array of the matched nodes
7398          */
7399         query : function(selector, unique){
7400             return Roo.DomQuery.select(selector, this.dom);
7401         },
7402
7403         /**
7404          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7405          * @param {String} selector The CSS selector
7406          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7407          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7408          */
7409         child : function(selector, returnDom){
7410             var n = Roo.DomQuery.selectNode(selector, this.dom);
7411             return returnDom ? n : Roo.get(n);
7412         },
7413
7414         /**
7415          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7416          * @param {String} selector The CSS selector
7417          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7418          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7419          */
7420         down : function(selector, returnDom){
7421             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7422             return returnDom ? n : Roo.get(n);
7423         },
7424
7425         /**
7426          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7427          * @param {String} group The group the DD object is member of
7428          * @param {Object} config The DD config object
7429          * @param {Object} overrides An object containing methods to override/implement on the DD object
7430          * @return {Roo.dd.DD} The DD object
7431          */
7432         initDD : function(group, config, overrides){
7433             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7434             return Roo.apply(dd, overrides);
7435         },
7436
7437         /**
7438          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7439          * @param {String} group The group the DDProxy object is member of
7440          * @param {Object} config The DDProxy config object
7441          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7442          * @return {Roo.dd.DDProxy} The DDProxy object
7443          */
7444         initDDProxy : function(group, config, overrides){
7445             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7446             return Roo.apply(dd, overrides);
7447         },
7448
7449         /**
7450          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7451          * @param {String} group The group the DDTarget object is member of
7452          * @param {Object} config The DDTarget config object
7453          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7454          * @return {Roo.dd.DDTarget} The DDTarget object
7455          */
7456         initDDTarget : function(group, config, overrides){
7457             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7458             return Roo.apply(dd, overrides);
7459         },
7460
7461         /**
7462          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7463          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7464          * @param {Boolean} visible Whether the element is visible
7465          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7466          * @return {Roo.Element} this
7467          */
7468          setVisible : function(visible, animate){
7469             if(!animate || !A){
7470                 if(this.visibilityMode == El.DISPLAY){
7471                     this.setDisplayed(visible);
7472                 }else{
7473                     this.fixDisplay();
7474                     this.dom.style.visibility = visible ? "visible" : "hidden";
7475                 }
7476             }else{
7477                 // closure for composites
7478                 var dom = this.dom;
7479                 var visMode = this.visibilityMode;
7480                 if(visible){
7481                     this.setOpacity(.01);
7482                     this.setVisible(true);
7483                 }
7484                 this.anim({opacity: { to: (visible?1:0) }},
7485                       this.preanim(arguments, 1),
7486                       null, .35, 'easeIn', function(){
7487                          if(!visible){
7488                              if(visMode == El.DISPLAY){
7489                                  dom.style.display = "none";
7490                              }else{
7491                                  dom.style.visibility = "hidden";
7492                              }
7493                              Roo.get(dom).setOpacity(1);
7494                          }
7495                      });
7496             }
7497             return this;
7498         },
7499
7500         /**
7501          * Returns true if display is not "none"
7502          * @return {Boolean}
7503          */
7504         isDisplayed : function() {
7505             return this.getStyle("display") != "none";
7506         },
7507
7508         /**
7509          * Toggles the element's visibility or display, depending on visibility mode.
7510          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7511          * @return {Roo.Element} this
7512          */
7513         toggle : function(animate){
7514             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7515             return this;
7516         },
7517
7518         /**
7519          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7520          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7521          * @return {Roo.Element} this
7522          */
7523         setDisplayed : function(value) {
7524             if(typeof value == "boolean"){
7525                value = value ? this.originalDisplay : "none";
7526             }
7527             this.setStyle("display", value);
7528             return this;
7529         },
7530
7531         /**
7532          * Tries to focus the element. Any exceptions are caught and ignored.
7533          * @return {Roo.Element} this
7534          */
7535         focus : function() {
7536             try{
7537                 this.dom.focus();
7538             }catch(e){}
7539             return this;
7540         },
7541
7542         /**
7543          * Tries to blur the element. Any exceptions are caught and ignored.
7544          * @return {Roo.Element} this
7545          */
7546         blur : function() {
7547             try{
7548                 this.dom.blur();
7549             }catch(e){}
7550             return this;
7551         },
7552
7553         /**
7554          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7555          * @param {String/Array} className The CSS class to add, or an array of classes
7556          * @return {Roo.Element} this
7557          */
7558         addClass : function(className){
7559             if(className instanceof Array){
7560                 for(var i = 0, len = className.length; i < len; i++) {
7561                     this.addClass(className[i]);
7562                 }
7563             }else{
7564                 if(className && !this.hasClass(className)){
7565                     this.dom.className = this.dom.className + " " + className;
7566                 }
7567             }
7568             return this;
7569         },
7570
7571         /**
7572          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7573          * @param {String/Array} className The CSS class to add, or an array of classes
7574          * @return {Roo.Element} this
7575          */
7576         radioClass : function(className){
7577             var siblings = this.dom.parentNode.childNodes;
7578             for(var i = 0; i < siblings.length; i++) {
7579                 var s = siblings[i];
7580                 if(s.nodeType == 1){
7581                     Roo.get(s).removeClass(className);
7582                 }
7583             }
7584             this.addClass(className);
7585             return this;
7586         },
7587
7588         /**
7589          * Removes one or more CSS classes from the element.
7590          * @param {String/Array} className The CSS class to remove, or an array of classes
7591          * @return {Roo.Element} this
7592          */
7593         removeClass : function(className){
7594             if(!className || !this.dom.className){
7595                 return this;
7596             }
7597             if(className instanceof Array){
7598                 for(var i = 0, len = className.length; i < len; i++) {
7599                     this.removeClass(className[i]);
7600                 }
7601             }else{
7602                 if(this.hasClass(className)){
7603                     var re = this.classReCache[className];
7604                     if (!re) {
7605                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7606                        this.classReCache[className] = re;
7607                     }
7608                     this.dom.className =
7609                         this.dom.className.replace(re, " ");
7610                 }
7611             }
7612             return this;
7613         },
7614
7615         // private
7616         classReCache: {},
7617
7618         /**
7619          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7620          * @param {String} className The CSS class to toggle
7621          * @return {Roo.Element} this
7622          */
7623         toggleClass : function(className){
7624             if(this.hasClass(className)){
7625                 this.removeClass(className);
7626             }else{
7627                 this.addClass(className);
7628             }
7629             return this;
7630         },
7631
7632         /**
7633          * Checks if the specified CSS class exists on this element's DOM node.
7634          * @param {String} className The CSS class to check for
7635          * @return {Boolean} True if the class exists, else false
7636          */
7637         hasClass : function(className){
7638             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7639         },
7640
7641         /**
7642          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7643          * @param {String} oldClassName The CSS class to replace
7644          * @param {String} newClassName The replacement CSS class
7645          * @return {Roo.Element} this
7646          */
7647         replaceClass : function(oldClassName, newClassName){
7648             this.removeClass(oldClassName);
7649             this.addClass(newClassName);
7650             return this;
7651         },
7652
7653         /**
7654          * Returns an object with properties matching the styles requested.
7655          * For example, el.getStyles('color', 'font-size', 'width') might return
7656          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7657          * @param {String} style1 A style name
7658          * @param {String} style2 A style name
7659          * @param {String} etc.
7660          * @return {Object} The style object
7661          */
7662         getStyles : function(){
7663             var a = arguments, len = a.length, r = {};
7664             for(var i = 0; i < len; i++){
7665                 r[a[i]] = this.getStyle(a[i]);
7666             }
7667             return r;
7668         },
7669
7670         /**
7671          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7672          * @param {String} property The style property whose value is returned.
7673          * @return {String} The current value of the style property for this element.
7674          */
7675         getStyle : function(){
7676             return view && view.getComputedStyle ?
7677                 function(prop){
7678                     var el = this.dom, v, cs, camel;
7679                     if(prop == 'float'){
7680                         prop = "cssFloat";
7681                     }
7682                     if(el.style && (v = el.style[prop])){
7683                         return v;
7684                     }
7685                     if(cs = view.getComputedStyle(el, "")){
7686                         if(!(camel = propCache[prop])){
7687                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7688                         }
7689                         return cs[camel];
7690                     }
7691                     return null;
7692                 } :
7693                 function(prop){
7694                     var el = this.dom, v, cs, camel;
7695                     if(prop == 'opacity'){
7696                         if(typeof el.style.filter == 'string'){
7697                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7698                             if(m){
7699                                 var fv = parseFloat(m[1]);
7700                                 if(!isNaN(fv)){
7701                                     return fv ? fv / 100 : 0;
7702                                 }
7703                             }
7704                         }
7705                         return 1;
7706                     }else if(prop == 'float'){
7707                         prop = "styleFloat";
7708                     }
7709                     if(!(camel = propCache[prop])){
7710                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7711                     }
7712                     if(v = el.style[camel]){
7713                         return v;
7714                     }
7715                     if(cs = el.currentStyle){
7716                         return cs[camel];
7717                     }
7718                     return null;
7719                 };
7720         }(),
7721
7722         /**
7723          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7724          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7725          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7726          * @return {Roo.Element} this
7727          */
7728         setStyle : function(prop, value){
7729             if(typeof prop == "string"){
7730                 
7731                 if (prop == 'float') {
7732                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7733                     return this;
7734                 }
7735                 
7736                 var camel;
7737                 if(!(camel = propCache[prop])){
7738                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7739                 }
7740                 
7741                 if(camel == 'opacity') {
7742                     this.setOpacity(value);
7743                 }else{
7744                     this.dom.style[camel] = value;
7745                 }
7746             }else{
7747                 for(var style in prop){
7748                     if(typeof prop[style] != "function"){
7749                        this.setStyle(style, prop[style]);
7750                     }
7751                 }
7752             }
7753             return this;
7754         },
7755
7756         /**
7757          * More flexible version of {@link #setStyle} for setting style properties.
7758          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7759          * a function which returns such a specification.
7760          * @return {Roo.Element} this
7761          */
7762         applyStyles : function(style){
7763             Roo.DomHelper.applyStyles(this.dom, style);
7764             return this;
7765         },
7766
7767         /**
7768           * 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).
7769           * @return {Number} The X position of the element
7770           */
7771         getX : function(){
7772             return D.getX(this.dom);
7773         },
7774
7775         /**
7776           * 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).
7777           * @return {Number} The Y position of the element
7778           */
7779         getY : function(){
7780             return D.getY(this.dom);
7781         },
7782
7783         /**
7784           * 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).
7785           * @return {Array} The XY position of the element
7786           */
7787         getXY : function(){
7788             return D.getXY(this.dom);
7789         },
7790
7791         /**
7792          * 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).
7793          * @param {Number} The X position of the element
7794          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7795          * @return {Roo.Element} this
7796          */
7797         setX : function(x, animate){
7798             if(!animate || !A){
7799                 D.setX(this.dom, x);
7800             }else{
7801                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7802             }
7803             return this;
7804         },
7805
7806         /**
7807          * 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).
7808          * @param {Number} The Y position of the element
7809          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7810          * @return {Roo.Element} this
7811          */
7812         setY : function(y, animate){
7813             if(!animate || !A){
7814                 D.setY(this.dom, y);
7815             }else{
7816                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7817             }
7818             return this;
7819         },
7820
7821         /**
7822          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7823          * @param {String} left The left CSS property value
7824          * @return {Roo.Element} this
7825          */
7826         setLeft : function(left){
7827             this.setStyle("left", this.addUnits(left));
7828             return this;
7829         },
7830
7831         /**
7832          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7833          * @param {String} top The top CSS property value
7834          * @return {Roo.Element} this
7835          */
7836         setTop : function(top){
7837             this.setStyle("top", this.addUnits(top));
7838             return this;
7839         },
7840
7841         /**
7842          * Sets the element's CSS right style.
7843          * @param {String} right The right CSS property value
7844          * @return {Roo.Element} this
7845          */
7846         setRight : function(right){
7847             this.setStyle("right", this.addUnits(right));
7848             return this;
7849         },
7850
7851         /**
7852          * Sets the element's CSS bottom style.
7853          * @param {String} bottom The bottom CSS property value
7854          * @return {Roo.Element} this
7855          */
7856         setBottom : function(bottom){
7857             this.setStyle("bottom", this.addUnits(bottom));
7858             return this;
7859         },
7860
7861         /**
7862          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7863          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7864          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7865          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7866          * @return {Roo.Element} this
7867          */
7868         setXY : function(pos, animate){
7869             if(!animate || !A){
7870                 D.setXY(this.dom, pos);
7871             }else{
7872                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7873             }
7874             return this;
7875         },
7876
7877         /**
7878          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7879          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7880          * @param {Number} x X value for new position (coordinates are page-based)
7881          * @param {Number} y Y value for new position (coordinates are page-based)
7882          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7883          * @return {Roo.Element} this
7884          */
7885         setLocation : function(x, y, animate){
7886             this.setXY([x, y], this.preanim(arguments, 2));
7887             return this;
7888         },
7889
7890         /**
7891          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7892          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7893          * @param {Number} x X value for new position (coordinates are page-based)
7894          * @param {Number} y Y value for new position (coordinates are page-based)
7895          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7896          * @return {Roo.Element} this
7897          */
7898         moveTo : function(x, y, animate){
7899             this.setXY([x, y], this.preanim(arguments, 2));
7900             return this;
7901         },
7902
7903         /**
7904          * Returns the region of the given element.
7905          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7906          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7907          */
7908         getRegion : function(){
7909             return D.getRegion(this.dom);
7910         },
7911
7912         /**
7913          * Returns the offset height of the element
7914          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7915          * @return {Number} The element's height
7916          */
7917         getHeight : function(contentHeight){
7918             var h = this.dom.offsetHeight || 0;
7919             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7920         },
7921
7922         /**
7923          * Returns the offset width of the element
7924          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7925          * @return {Number} The element's width
7926          */
7927         getWidth : function(contentWidth){
7928             var w = this.dom.offsetWidth || 0;
7929             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7930         },
7931
7932         /**
7933          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7934          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7935          * if a height has not been set using CSS.
7936          * @return {Number}
7937          */
7938         getComputedHeight : function(){
7939             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7940             if(!h){
7941                 h = parseInt(this.getStyle('height'), 10) || 0;
7942                 if(!this.isBorderBox()){
7943                     h += this.getFrameWidth('tb');
7944                 }
7945             }
7946             return h;
7947         },
7948
7949         /**
7950          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7951          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7952          * if a width has not been set using CSS.
7953          * @return {Number}
7954          */
7955         getComputedWidth : function(){
7956             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7957             if(!w){
7958                 w = parseInt(this.getStyle('width'), 10) || 0;
7959                 if(!this.isBorderBox()){
7960                     w += this.getFrameWidth('lr');
7961                 }
7962             }
7963             return w;
7964         },
7965
7966         /**
7967          * Returns the size of the element.
7968          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7969          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7970          */
7971         getSize : function(contentSize){
7972             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7973         },
7974
7975         /**
7976          * Returns the width and height of the viewport.
7977          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7978          */
7979         getViewSize : function(){
7980             var d = this.dom, doc = document, aw = 0, ah = 0;
7981             if(d == doc || d == doc.body){
7982                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7983             }else{
7984                 return {
7985                     width : d.clientWidth,
7986                     height: d.clientHeight
7987                 };
7988             }
7989         },
7990
7991         /**
7992          * Returns the value of the "value" attribute
7993          * @param {Boolean} asNumber true to parse the value as a number
7994          * @return {String/Number}
7995          */
7996         getValue : function(asNumber){
7997             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7998         },
7999
8000         // private
8001         adjustWidth : function(width){
8002             if(typeof width == "number"){
8003                 if(this.autoBoxAdjust && !this.isBorderBox()){
8004                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8005                 }
8006                 if(width < 0){
8007                     width = 0;
8008                 }
8009             }
8010             return width;
8011         },
8012
8013         // private
8014         adjustHeight : function(height){
8015             if(typeof height == "number"){
8016                if(this.autoBoxAdjust && !this.isBorderBox()){
8017                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8018                }
8019                if(height < 0){
8020                    height = 0;
8021                }
8022             }
8023             return height;
8024         },
8025
8026         /**
8027          * Set the width of the element
8028          * @param {Number} width The new width
8029          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032         setWidth : function(width, animate){
8033             width = this.adjustWidth(width);
8034             if(!animate || !A){
8035                 this.dom.style.width = this.addUnits(width);
8036             }else{
8037                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8038             }
8039             return this;
8040         },
8041
8042         /**
8043          * Set the height of the element
8044          * @param {Number} height The new height
8045          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8046          * @return {Roo.Element} this
8047          */
8048          setHeight : function(height, animate){
8049             height = this.adjustHeight(height);
8050             if(!animate || !A){
8051                 this.dom.style.height = this.addUnits(height);
8052             }else{
8053                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8054             }
8055             return this;
8056         },
8057
8058         /**
8059          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8060          * @param {Number} width The new width
8061          * @param {Number} height The new height
8062          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8063          * @return {Roo.Element} this
8064          */
8065          setSize : function(width, height, animate){
8066             if(typeof width == "object"){ // in case of object from getSize()
8067                 height = width.height; width = width.width;
8068             }
8069             width = this.adjustWidth(width); height = this.adjustHeight(height);
8070             if(!animate || !A){
8071                 this.dom.style.width = this.addUnits(width);
8072                 this.dom.style.height = this.addUnits(height);
8073             }else{
8074                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8075             }
8076             return this;
8077         },
8078
8079         /**
8080          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8081          * @param {Number} x X value for new position (coordinates are page-based)
8082          * @param {Number} y Y value for new position (coordinates are page-based)
8083          * @param {Number} width The new width
8084          * @param {Number} height The new height
8085          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8086          * @return {Roo.Element} this
8087          */
8088         setBounds : function(x, y, width, height, animate){
8089             if(!animate || !A){
8090                 this.setSize(width, height);
8091                 this.setLocation(x, y);
8092             }else{
8093                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8094                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8095                               this.preanim(arguments, 4), 'motion');
8096             }
8097             return this;
8098         },
8099
8100         /**
8101          * 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.
8102          * @param {Roo.lib.Region} region The region to fill
8103          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8104          * @return {Roo.Element} this
8105          */
8106         setRegion : function(region, animate){
8107             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8108             return this;
8109         },
8110
8111         /**
8112          * Appends an event handler
8113          *
8114          * @param {String}   eventName     The type of event to append
8115          * @param {Function} fn        The method the event invokes
8116          * @param {Object} scope       (optional) The scope (this object) of the fn
8117          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8118          */
8119         addListener : function(eventName, fn, scope, options){
8120             if (this.dom) {
8121                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8122             }
8123         },
8124
8125         /**
8126          * Removes an event handler from this element
8127          * @param {String} eventName the type of event to remove
8128          * @param {Function} fn the method the event invokes
8129          * @return {Roo.Element} this
8130          */
8131         removeListener : function(eventName, fn){
8132             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8133             return this;
8134         },
8135
8136         /**
8137          * Removes all previous added listeners from this element
8138          * @return {Roo.Element} this
8139          */
8140         removeAllListeners : function(){
8141             E.purgeElement(this.dom);
8142             return this;
8143         },
8144
8145         relayEvent : function(eventName, observable){
8146             this.on(eventName, function(e){
8147                 observable.fireEvent(eventName, e);
8148             });
8149         },
8150
8151         /**
8152          * Set the opacity of the element
8153          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8154          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8155          * @return {Roo.Element} this
8156          */
8157          setOpacity : function(opacity, animate){
8158             if(!animate || !A){
8159                 var s = this.dom.style;
8160                 if(Roo.isIE){
8161                     s.zoom = 1;
8162                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8163                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8164                 }else{
8165                     s.opacity = opacity;
8166                 }
8167             }else{
8168                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8169             }
8170             return this;
8171         },
8172
8173         /**
8174          * Gets the left X coordinate
8175          * @param {Boolean} local True to get the local css position instead of page coordinate
8176          * @return {Number}
8177          */
8178         getLeft : function(local){
8179             if(!local){
8180                 return this.getX();
8181             }else{
8182                 return parseInt(this.getStyle("left"), 10) || 0;
8183             }
8184         },
8185
8186         /**
8187          * Gets the right X coordinate of the element (element X position + element width)
8188          * @param {Boolean} local True to get the local css position instead of page coordinate
8189          * @return {Number}
8190          */
8191         getRight : function(local){
8192             if(!local){
8193                 return this.getX() + this.getWidth();
8194             }else{
8195                 return (this.getLeft(true) + this.getWidth()) || 0;
8196             }
8197         },
8198
8199         /**
8200          * Gets the top Y coordinate
8201          * @param {Boolean} local True to get the local css position instead of page coordinate
8202          * @return {Number}
8203          */
8204         getTop : function(local) {
8205             if(!local){
8206                 return this.getY();
8207             }else{
8208                 return parseInt(this.getStyle("top"), 10) || 0;
8209             }
8210         },
8211
8212         /**
8213          * Gets the bottom Y coordinate of the element (element Y position + element height)
8214          * @param {Boolean} local True to get the local css position instead of page coordinate
8215          * @return {Number}
8216          */
8217         getBottom : function(local){
8218             if(!local){
8219                 return this.getY() + this.getHeight();
8220             }else{
8221                 return (this.getTop(true) + this.getHeight()) || 0;
8222             }
8223         },
8224
8225         /**
8226         * Initializes positioning on this element. If a desired position is not passed, it will make the
8227         * the element positioned relative IF it is not already positioned.
8228         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8229         * @param {Number} zIndex (optional) The zIndex to apply
8230         * @param {Number} x (optional) Set the page X position
8231         * @param {Number} y (optional) Set the page Y position
8232         */
8233         position : function(pos, zIndex, x, y){
8234             if(!pos){
8235                if(this.getStyle('position') == 'static'){
8236                    this.setStyle('position', 'relative');
8237                }
8238             }else{
8239                 this.setStyle("position", pos);
8240             }
8241             if(zIndex){
8242                 this.setStyle("z-index", zIndex);
8243             }
8244             if(x !== undefined && y !== undefined){
8245                 this.setXY([x, y]);
8246             }else if(x !== undefined){
8247                 this.setX(x);
8248             }else if(y !== undefined){
8249                 this.setY(y);
8250             }
8251         },
8252
8253         /**
8254         * Clear positioning back to the default when the document was loaded
8255         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8256         * @return {Roo.Element} this
8257          */
8258         clearPositioning : function(value){
8259             value = value ||'';
8260             this.setStyle({
8261                 "left": value,
8262                 "right": value,
8263                 "top": value,
8264                 "bottom": value,
8265                 "z-index": "",
8266                 "position" : "static"
8267             });
8268             return this;
8269         },
8270
8271         /**
8272         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8273         * snapshot before performing an update and then restoring the element.
8274         * @return {Object}
8275         */
8276         getPositioning : function(){
8277             var l = this.getStyle("left");
8278             var t = this.getStyle("top");
8279             return {
8280                 "position" : this.getStyle("position"),
8281                 "left" : l,
8282                 "right" : l ? "" : this.getStyle("right"),
8283                 "top" : t,
8284                 "bottom" : t ? "" : this.getStyle("bottom"),
8285                 "z-index" : this.getStyle("z-index")
8286             };
8287         },
8288
8289         /**
8290          * Gets the width of the border(s) for the specified side(s)
8291          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8292          * passing lr would get the border (l)eft width + the border (r)ight width.
8293          * @return {Number} The width of the sides passed added together
8294          */
8295         getBorderWidth : function(side){
8296             return this.addStyles(side, El.borders);
8297         },
8298
8299         /**
8300          * Gets the width of the padding(s) for the specified side(s)
8301          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8302          * passing lr would get the padding (l)eft + the padding (r)ight.
8303          * @return {Number} The padding of the sides passed added together
8304          */
8305         getPadding : function(side){
8306             return this.addStyles(side, El.paddings);
8307         },
8308
8309         /**
8310         * Set positioning with an object returned by getPositioning().
8311         * @param {Object} posCfg
8312         * @return {Roo.Element} this
8313          */
8314         setPositioning : function(pc){
8315             this.applyStyles(pc);
8316             if(pc.right == "auto"){
8317                 this.dom.style.right = "";
8318             }
8319             if(pc.bottom == "auto"){
8320                 this.dom.style.bottom = "";
8321             }
8322             return this;
8323         },
8324
8325         // private
8326         fixDisplay : function(){
8327             if(this.getStyle("display") == "none"){
8328                 this.setStyle("visibility", "hidden");
8329                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8330                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8331                     this.setStyle("display", "block");
8332                 }
8333             }
8334         },
8335
8336         /**
8337          * Quick set left and top adding default units
8338          * @param {String} left The left CSS property value
8339          * @param {String} top The top CSS property value
8340          * @return {Roo.Element} this
8341          */
8342          setLeftTop : function(left, top){
8343             this.dom.style.left = this.addUnits(left);
8344             this.dom.style.top = this.addUnits(top);
8345             return this;
8346         },
8347
8348         /**
8349          * Move this element relative to its current position.
8350          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8351          * @param {Number} distance How far to move the element in pixels
8352          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8353          * @return {Roo.Element} this
8354          */
8355          move : function(direction, distance, animate){
8356             var xy = this.getXY();
8357             direction = direction.toLowerCase();
8358             switch(direction){
8359                 case "l":
8360                 case "left":
8361                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8362                     break;
8363                case "r":
8364                case "right":
8365                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8366                     break;
8367                case "t":
8368                case "top":
8369                case "up":
8370                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8371                     break;
8372                case "b":
8373                case "bottom":
8374                case "down":
8375                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8376                     break;
8377             }
8378             return this;
8379         },
8380
8381         /**
8382          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8383          * @return {Roo.Element} this
8384          */
8385         clip : function(){
8386             if(!this.isClipped){
8387                this.isClipped = true;
8388                this.originalClip = {
8389                    "o": this.getStyle("overflow"),
8390                    "x": this.getStyle("overflow-x"),
8391                    "y": this.getStyle("overflow-y")
8392                };
8393                this.setStyle("overflow", "hidden");
8394                this.setStyle("overflow-x", "hidden");
8395                this.setStyle("overflow-y", "hidden");
8396             }
8397             return this;
8398         },
8399
8400         /**
8401          *  Return clipping (overflow) to original clipping before clip() was called
8402          * @return {Roo.Element} this
8403          */
8404         unclip : function(){
8405             if(this.isClipped){
8406                 this.isClipped = false;
8407                 var o = this.originalClip;
8408                 if(o.o){this.setStyle("overflow", o.o);}
8409                 if(o.x){this.setStyle("overflow-x", o.x);}
8410                 if(o.y){this.setStyle("overflow-y", o.y);}
8411             }
8412             return this;
8413         },
8414
8415
8416         /**
8417          * Gets the x,y coordinates specified by the anchor position on the element.
8418          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8419          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8420          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8421          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8422          * @return {Array} [x, y] An array containing the element's x and y coordinates
8423          */
8424         getAnchorXY : function(anchor, local, s){
8425             //Passing a different size is useful for pre-calculating anchors,
8426             //especially for anchored animations that change the el size.
8427
8428             var w, h, vp = false;
8429             if(!s){
8430                 var d = this.dom;
8431                 if(d == document.body || d == document){
8432                     vp = true;
8433                     w = D.getViewWidth(); h = D.getViewHeight();
8434                 }else{
8435                     w = this.getWidth(); h = this.getHeight();
8436                 }
8437             }else{
8438                 w = s.width;  h = s.height;
8439             }
8440             var x = 0, y = 0, r = Math.round;
8441             switch((anchor || "tl").toLowerCase()){
8442                 case "c":
8443                     x = r(w*.5);
8444                     y = r(h*.5);
8445                 break;
8446                 case "t":
8447                     x = r(w*.5);
8448                     y = 0;
8449                 break;
8450                 case "l":
8451                     x = 0;
8452                     y = r(h*.5);
8453                 break;
8454                 case "r":
8455                     x = w;
8456                     y = r(h*.5);
8457                 break;
8458                 case "b":
8459                     x = r(w*.5);
8460                     y = h;
8461                 break;
8462                 case "tl":
8463                     x = 0;
8464                     y = 0;
8465                 break;
8466                 case "bl":
8467                     x = 0;
8468                     y = h;
8469                 break;
8470                 case "br":
8471                     x = w;
8472                     y = h;
8473                 break;
8474                 case "tr":
8475                     x = w;
8476                     y = 0;
8477                 break;
8478             }
8479             if(local === true){
8480                 return [x, y];
8481             }
8482             if(vp){
8483                 var sc = this.getScroll();
8484                 return [x + sc.left, y + sc.top];
8485             }
8486             //Add the element's offset xy
8487             var o = this.getXY();
8488             return [x+o[0], y+o[1]];
8489         },
8490
8491         /**
8492          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8493          * supported position values.
8494          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8495          * @param {String} position The position to align to.
8496          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8497          * @return {Array} [x, y]
8498          */
8499         getAlignToXY : function(el, p, o){
8500             el = Roo.get(el);
8501             var d = this.dom;
8502             if(!el.dom){
8503                 throw "Element.alignTo with an element that doesn't exist";
8504             }
8505             var c = false; //constrain to viewport
8506             var p1 = "", p2 = "";
8507             o = o || [0,0];
8508
8509             if(!p){
8510                 p = "tl-bl";
8511             }else if(p == "?"){
8512                 p = "tl-bl?";
8513             }else if(p.indexOf("-") == -1){
8514                 p = "tl-" + p;
8515             }
8516             p = p.toLowerCase();
8517             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8518             if(!m){
8519                throw "Element.alignTo with an invalid alignment " + p;
8520             }
8521             p1 = m[1]; p2 = m[2]; c = !!m[3];
8522
8523             //Subtract the aligned el's internal xy from the target's offset xy
8524             //plus custom offset to get the aligned el's new offset xy
8525             var a1 = this.getAnchorXY(p1, true);
8526             var a2 = el.getAnchorXY(p2, false);
8527             var x = a2[0] - a1[0] + o[0];
8528             var y = a2[1] - a1[1] + o[1];
8529             if(c){
8530                 //constrain the aligned el to viewport if necessary
8531                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8532                 // 5px of margin for ie
8533                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8534
8535                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8536                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8537                 //otherwise swap the aligned el to the opposite border of the target.
8538                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8539                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8540                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8541                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8542
8543                var doc = document;
8544                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8545                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8546
8547                if((x+w) > dw + scrollX){
8548                     x = swapX ? r.left-w : dw+scrollX-w;
8549                 }
8550                if(x < scrollX){
8551                    x = swapX ? r.right : scrollX;
8552                }
8553                if((y+h) > dh + scrollY){
8554                     y = swapY ? r.top-h : dh+scrollY-h;
8555                 }
8556                if (y < scrollY){
8557                    y = swapY ? r.bottom : scrollY;
8558                }
8559             }
8560             return [x,y];
8561         },
8562
8563         // private
8564         getConstrainToXY : function(){
8565             var os = {top:0, left:0, bottom:0, right: 0};
8566
8567             return function(el, local, offsets, proposedXY){
8568                 el = Roo.get(el);
8569                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8570
8571                 var vw, vh, vx = 0, vy = 0;
8572                 if(el.dom == document.body || el.dom == document){
8573                     vw = Roo.lib.Dom.getViewWidth();
8574                     vh = Roo.lib.Dom.getViewHeight();
8575                 }else{
8576                     vw = el.dom.clientWidth;
8577                     vh = el.dom.clientHeight;
8578                     if(!local){
8579                         var vxy = el.getXY();
8580                         vx = vxy[0];
8581                         vy = vxy[1];
8582                     }
8583                 }
8584
8585                 var s = el.getScroll();
8586
8587                 vx += offsets.left + s.left;
8588                 vy += offsets.top + s.top;
8589
8590                 vw -= offsets.right;
8591                 vh -= offsets.bottom;
8592
8593                 var vr = vx+vw;
8594                 var vb = vy+vh;
8595
8596                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8597                 var x = xy[0], y = xy[1];
8598                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8599
8600                 // only move it if it needs it
8601                 var moved = false;
8602
8603                 // first validate right/bottom
8604                 if((x + w) > vr){
8605                     x = vr - w;
8606                     moved = true;
8607                 }
8608                 if((y + h) > vb){
8609                     y = vb - h;
8610                     moved = true;
8611                 }
8612                 // then make sure top/left isn't negative
8613                 if(x < vx){
8614                     x = vx;
8615                     moved = true;
8616                 }
8617                 if(y < vy){
8618                     y = vy;
8619                     moved = true;
8620                 }
8621                 return moved ? [x, y] : false;
8622             };
8623         }(),
8624
8625         // private
8626         adjustForConstraints : function(xy, parent, offsets){
8627             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8628         },
8629
8630         /**
8631          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8632          * document it aligns it to the viewport.
8633          * The position parameter is optional, and can be specified in any one of the following formats:
8634          * <ul>
8635          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8636          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8637          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8638          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8639          *   <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
8640          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8641          * </ul>
8642          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8643          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8644          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8645          * that specified in order to enforce the viewport constraints.
8646          * Following are all of the supported anchor positions:
8647     <pre>
8648     Value  Description
8649     -----  -----------------------------
8650     tl     The top left corner (default)
8651     t      The center of the top edge
8652     tr     The top right corner
8653     l      The center of the left edge
8654     c      In the center of the element
8655     r      The center of the right edge
8656     bl     The bottom left corner
8657     b      The center of the bottom edge
8658     br     The bottom right corner
8659     </pre>
8660     Example Usage:
8661     <pre><code>
8662     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8663     el.alignTo("other-el");
8664
8665     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8666     el.alignTo("other-el", "tr?");
8667
8668     // align the bottom right corner of el with the center left edge of other-el
8669     el.alignTo("other-el", "br-l?");
8670
8671     // align the center of el with the bottom left corner of other-el and
8672     // adjust the x position by -6 pixels (and the y position by 0)
8673     el.alignTo("other-el", "c-bl", [-6, 0]);
8674     </code></pre>
8675          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8676          * @param {String} position The position to align to.
8677          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8678          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8679          * @return {Roo.Element} this
8680          */
8681         alignTo : function(element, position, offsets, animate){
8682             var xy = this.getAlignToXY(element, position, offsets);
8683             this.setXY(xy, this.preanim(arguments, 3));
8684             return this;
8685         },
8686
8687         /**
8688          * Anchors an element to another element and realigns it when the window is resized.
8689          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8690          * @param {String} position The position to align to.
8691          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8692          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8693          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8694          * is a number, it is used as the buffer delay (defaults to 50ms).
8695          * @param {Function} callback The function to call after the animation finishes
8696          * @return {Roo.Element} this
8697          */
8698         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8699             var action = function(){
8700                 this.alignTo(el, alignment, offsets, animate);
8701                 Roo.callback(callback, this);
8702             };
8703             Roo.EventManager.onWindowResize(action, this);
8704             var tm = typeof monitorScroll;
8705             if(tm != 'undefined'){
8706                 Roo.EventManager.on(window, 'scroll', action, this,
8707                     {buffer: tm == 'number' ? monitorScroll : 50});
8708             }
8709             action.call(this); // align immediately
8710             return this;
8711         },
8712         /**
8713          * Clears any opacity settings from this element. Required in some cases for IE.
8714          * @return {Roo.Element} this
8715          */
8716         clearOpacity : function(){
8717             if (window.ActiveXObject) {
8718                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8719                     this.dom.style.filter = "";
8720                 }
8721             } else {
8722                 this.dom.style.opacity = "";
8723                 this.dom.style["-moz-opacity"] = "";
8724                 this.dom.style["-khtml-opacity"] = "";
8725             }
8726             return this;
8727         },
8728
8729         /**
8730          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8731          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8732          * @return {Roo.Element} this
8733          */
8734         hide : function(animate){
8735             this.setVisible(false, this.preanim(arguments, 0));
8736             return this;
8737         },
8738
8739         /**
8740         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8741         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8742          * @return {Roo.Element} this
8743          */
8744         show : function(animate){
8745             this.setVisible(true, this.preanim(arguments, 0));
8746             return this;
8747         },
8748
8749         /**
8750          * @private Test if size has a unit, otherwise appends the default
8751          */
8752         addUnits : function(size){
8753             return Roo.Element.addUnits(size, this.defaultUnit);
8754         },
8755
8756         /**
8757          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8758          * @return {Roo.Element} this
8759          */
8760         beginMeasure : function(){
8761             var el = this.dom;
8762             if(el.offsetWidth || el.offsetHeight){
8763                 return this; // offsets work already
8764             }
8765             var changed = [];
8766             var p = this.dom, b = document.body; // start with this element
8767             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8768                 var pe = Roo.get(p);
8769                 if(pe.getStyle('display') == 'none'){
8770                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8771                     p.style.visibility = "hidden";
8772                     p.style.display = "block";
8773                 }
8774                 p = p.parentNode;
8775             }
8776             this._measureChanged = changed;
8777             return this;
8778
8779         },
8780
8781         /**
8782          * Restores displays to before beginMeasure was called
8783          * @return {Roo.Element} this
8784          */
8785         endMeasure : function(){
8786             var changed = this._measureChanged;
8787             if(changed){
8788                 for(var i = 0, len = changed.length; i < len; i++) {
8789                     var r = changed[i];
8790                     r.el.style.visibility = r.visibility;
8791                     r.el.style.display = "none";
8792                 }
8793                 this._measureChanged = null;
8794             }
8795             return this;
8796         },
8797
8798         /**
8799         * Update the innerHTML of this element, optionally searching for and processing scripts
8800         * @param {String} html The new HTML
8801         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8802         * @param {Function} callback For async script loading you can be noticed when the update completes
8803         * @return {Roo.Element} this
8804          */
8805         update : function(html, loadScripts, callback){
8806             if(typeof html == "undefined"){
8807                 html = "";
8808             }
8809             if(loadScripts !== true){
8810                 this.dom.innerHTML = html;
8811                 if(typeof callback == "function"){
8812                     callback();
8813                 }
8814                 return this;
8815             }
8816             var id = Roo.id();
8817             var dom = this.dom;
8818
8819             html += '<span id="' + id + '"></span>';
8820
8821             E.onAvailable(id, function(){
8822                 var hd = document.getElementsByTagName("head")[0];
8823                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8824                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8825                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8826
8827                 var match;
8828                 while(match = re.exec(html)){
8829                     var attrs = match[1];
8830                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8831                     if(srcMatch && srcMatch[2]){
8832                        var s = document.createElement("script");
8833                        s.src = srcMatch[2];
8834                        var typeMatch = attrs.match(typeRe);
8835                        if(typeMatch && typeMatch[2]){
8836                            s.type = typeMatch[2];
8837                        }
8838                        hd.appendChild(s);
8839                     }else if(match[2] && match[2].length > 0){
8840                         if(window.execScript) {
8841                            window.execScript(match[2]);
8842                         } else {
8843                             /**
8844                              * eval:var:id
8845                              * eval:var:dom
8846                              * eval:var:html
8847                              * 
8848                              */
8849                            window.eval(match[2]);
8850                         }
8851                     }
8852                 }
8853                 var el = document.getElementById(id);
8854                 if(el){el.parentNode.removeChild(el);}
8855                 if(typeof callback == "function"){
8856                     callback();
8857                 }
8858             });
8859             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8860             return this;
8861         },
8862
8863         /**
8864          * Direct access to the UpdateManager update() method (takes the same parameters).
8865          * @param {String/Function} url The url for this request or a function to call to get the url
8866          * @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}
8867          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8868          * @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.
8869          * @return {Roo.Element} this
8870          */
8871         load : function(){
8872             var um = this.getUpdateManager();
8873             um.update.apply(um, arguments);
8874             return this;
8875         },
8876
8877         /**
8878         * Gets this element's UpdateManager
8879         * @return {Roo.UpdateManager} The UpdateManager
8880         */
8881         getUpdateManager : function(){
8882             if(!this.updateManager){
8883                 this.updateManager = new Roo.UpdateManager(this);
8884             }
8885             return this.updateManager;
8886         },
8887
8888         /**
8889          * Disables text selection for this element (normalized across browsers)
8890          * @return {Roo.Element} this
8891          */
8892         unselectable : function(){
8893             this.dom.unselectable = "on";
8894             this.swallowEvent("selectstart", true);
8895             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8896             this.addClass("x-unselectable");
8897             return this;
8898         },
8899
8900         /**
8901         * Calculates the x, y to center this element on the screen
8902         * @return {Array} The x, y values [x, y]
8903         */
8904         getCenterXY : function(){
8905             return this.getAlignToXY(document, 'c-c');
8906         },
8907
8908         /**
8909         * Centers the Element in either the viewport, or another Element.
8910         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8911         */
8912         center : function(centerIn){
8913             this.alignTo(centerIn || document, 'c-c');
8914             return this;
8915         },
8916
8917         /**
8918          * Tests various css rules/browsers to determine if this element uses a border box
8919          * @return {Boolean}
8920          */
8921         isBorderBox : function(){
8922             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8923         },
8924
8925         /**
8926          * Return a box {x, y, width, height} that can be used to set another elements
8927          * size/location to match this element.
8928          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8929          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8930          * @return {Object} box An object in the format {x, y, width, height}
8931          */
8932         getBox : function(contentBox, local){
8933             var xy;
8934             if(!local){
8935                 xy = this.getXY();
8936             }else{
8937                 var left = parseInt(this.getStyle("left"), 10) || 0;
8938                 var top = parseInt(this.getStyle("top"), 10) || 0;
8939                 xy = [left, top];
8940             }
8941             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8942             if(!contentBox){
8943                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8944             }else{
8945                 var l = this.getBorderWidth("l")+this.getPadding("l");
8946                 var r = this.getBorderWidth("r")+this.getPadding("r");
8947                 var t = this.getBorderWidth("t")+this.getPadding("t");
8948                 var b = this.getBorderWidth("b")+this.getPadding("b");
8949                 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)};
8950             }
8951             bx.right = bx.x + bx.width;
8952             bx.bottom = bx.y + bx.height;
8953             return bx;
8954         },
8955
8956         /**
8957          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8958          for more information about the sides.
8959          * @param {String} sides
8960          * @return {Number}
8961          */
8962         getFrameWidth : function(sides, onlyContentBox){
8963             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8964         },
8965
8966         /**
8967          * 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.
8968          * @param {Object} box The box to fill {x, y, width, height}
8969          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8970          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8971          * @return {Roo.Element} this
8972          */
8973         setBox : function(box, adjust, animate){
8974             var w = box.width, h = box.height;
8975             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8976                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8977                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8978             }
8979             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8980             return this;
8981         },
8982
8983         /**
8984          * Forces the browser to repaint this element
8985          * @return {Roo.Element} this
8986          */
8987          repaint : function(){
8988             var dom = this.dom;
8989             this.addClass("x-repaint");
8990             setTimeout(function(){
8991                 Roo.get(dom).removeClass("x-repaint");
8992             }, 1);
8993             return this;
8994         },
8995
8996         /**
8997          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8998          * then it returns the calculated width of the sides (see getPadding)
8999          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9000          * @return {Object/Number}
9001          */
9002         getMargins : function(side){
9003             if(!side){
9004                 return {
9005                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9006                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9007                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9008                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9009                 };
9010             }else{
9011                 return this.addStyles(side, El.margins);
9012              }
9013         },
9014
9015         // private
9016         addStyles : function(sides, styles){
9017             var val = 0, v, w;
9018             for(var i = 0, len = sides.length; i < len; i++){
9019                 v = this.getStyle(styles[sides.charAt(i)]);
9020                 if(v){
9021                      w = parseInt(v, 10);
9022                      if(w){ val += w; }
9023                 }
9024             }
9025             return val;
9026         },
9027
9028         /**
9029          * Creates a proxy element of this element
9030          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9031          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9032          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9033          * @return {Roo.Element} The new proxy element
9034          */
9035         createProxy : function(config, renderTo, matchBox){
9036             if(renderTo){
9037                 renderTo = Roo.getDom(renderTo);
9038             }else{
9039                 renderTo = document.body;
9040             }
9041             config = typeof config == "object" ?
9042                 config : {tag : "div", cls: config};
9043             var proxy = Roo.DomHelper.append(renderTo, config, true);
9044             if(matchBox){
9045                proxy.setBox(this.getBox());
9046             }
9047             return proxy;
9048         },
9049
9050         /**
9051          * Puts a mask over this element to disable user interaction. Requires core.css.
9052          * This method can only be applied to elements which accept child nodes.
9053          * @param {String} msg (optional) A message to display in the mask
9054          * @param {String} msgCls (optional) A css class to apply to the msg element
9055          * @return {Element} The mask  element
9056          */
9057         mask : function(msg, msgCls)
9058         {
9059             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9060                 this.setStyle("position", "relative");
9061             }
9062             if(!this._mask){
9063                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9064             }
9065             this.addClass("x-masked");
9066             this._mask.setDisplayed(true);
9067             
9068             // we wander
9069             var z = 0;
9070             var dom = this.dom;
9071             while (dom && dom.style) {
9072                 if (!isNaN(parseInt(dom.style.zIndex))) {
9073                     z = Math.max(z, parseInt(dom.style.zIndex));
9074                 }
9075                 dom = dom.parentNode;
9076             }
9077             // if we are masking the body - then it hides everything..
9078             if (this.dom == document.body) {
9079                 z = 1000000;
9080                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9081                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9082             }
9083            
9084             if(typeof msg == 'string'){
9085                 if(!this._maskMsg){
9086                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9087                 }
9088                 var mm = this._maskMsg;
9089                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9090                 if (mm.dom.firstChild) { // weird IE issue?
9091                     mm.dom.firstChild.innerHTML = msg;
9092                 }
9093                 mm.setDisplayed(true);
9094                 mm.center(this);
9095                 mm.setStyle('z-index', z + 102);
9096             }
9097             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9098                 this._mask.setHeight(this.getHeight());
9099             }
9100             this._mask.setStyle('z-index', z + 100);
9101             
9102             return this._mask;
9103         },
9104
9105         /**
9106          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9107          * it is cached for reuse.
9108          */
9109         unmask : function(removeEl){
9110             if(this._mask){
9111                 if(removeEl === true){
9112                     this._mask.remove();
9113                     delete this._mask;
9114                     if(this._maskMsg){
9115                         this._maskMsg.remove();
9116                         delete this._maskMsg;
9117                     }
9118                 }else{
9119                     this._mask.setDisplayed(false);
9120                     if(this._maskMsg){
9121                         this._maskMsg.setDisplayed(false);
9122                     }
9123                 }
9124             }
9125             this.removeClass("x-masked");
9126         },
9127
9128         /**
9129          * Returns true if this element is masked
9130          * @return {Boolean}
9131          */
9132         isMasked : function(){
9133             return this._mask && this._mask.isVisible();
9134         },
9135
9136         /**
9137          * Creates an iframe shim for this element to keep selects and other windowed objects from
9138          * showing through.
9139          * @return {Roo.Element} The new shim element
9140          */
9141         createShim : function(){
9142             var el = document.createElement('iframe');
9143             el.frameBorder = 'no';
9144             el.className = 'roo-shim';
9145             if(Roo.isIE && Roo.isSecure){
9146                 el.src = Roo.SSL_SECURE_URL;
9147             }
9148             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9149             shim.autoBoxAdjust = false;
9150             return shim;
9151         },
9152
9153         /**
9154          * Removes this element from the DOM and deletes it from the cache
9155          */
9156         remove : function(){
9157             if(this.dom.parentNode){
9158                 this.dom.parentNode.removeChild(this.dom);
9159             }
9160             delete El.cache[this.dom.id];
9161         },
9162
9163         /**
9164          * Sets up event handlers to add and remove a css class when the mouse is over this element
9165          * @param {String} className
9166          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9167          * mouseout events for children elements
9168          * @return {Roo.Element} this
9169          */
9170         addClassOnOver : function(className, preventFlicker){
9171             this.on("mouseover", function(){
9172                 Roo.fly(this, '_internal').addClass(className);
9173             }, this.dom);
9174             var removeFn = function(e){
9175                 if(preventFlicker !== true || !e.within(this, true)){
9176                     Roo.fly(this, '_internal').removeClass(className);
9177                 }
9178             };
9179             this.on("mouseout", removeFn, this.dom);
9180             return this;
9181         },
9182
9183         /**
9184          * Sets up event handlers to add and remove a css class when this element has the focus
9185          * @param {String} className
9186          * @return {Roo.Element} this
9187          */
9188         addClassOnFocus : function(className){
9189             this.on("focus", function(){
9190                 Roo.fly(this, '_internal').addClass(className);
9191             }, this.dom);
9192             this.on("blur", function(){
9193                 Roo.fly(this, '_internal').removeClass(className);
9194             }, this.dom);
9195             return this;
9196         },
9197         /**
9198          * 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)
9199          * @param {String} className
9200          * @return {Roo.Element} this
9201          */
9202         addClassOnClick : function(className){
9203             var dom = this.dom;
9204             this.on("mousedown", function(){
9205                 Roo.fly(dom, '_internal').addClass(className);
9206                 var d = Roo.get(document);
9207                 var fn = function(){
9208                     Roo.fly(dom, '_internal').removeClass(className);
9209                     d.removeListener("mouseup", fn);
9210                 };
9211                 d.on("mouseup", fn);
9212             });
9213             return this;
9214         },
9215
9216         /**
9217          * Stops the specified event from bubbling and optionally prevents the default action
9218          * @param {String} eventName
9219          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9220          * @return {Roo.Element} this
9221          */
9222         swallowEvent : function(eventName, preventDefault){
9223             var fn = function(e){
9224                 e.stopPropagation();
9225                 if(preventDefault){
9226                     e.preventDefault();
9227                 }
9228             };
9229             if(eventName instanceof Array){
9230                 for(var i = 0, len = eventName.length; i < len; i++){
9231                      this.on(eventName[i], fn);
9232                 }
9233                 return this;
9234             }
9235             this.on(eventName, fn);
9236             return this;
9237         },
9238
9239         /**
9240          * @private
9241          */
9242       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9243
9244         /**
9245          * Sizes this element to its parent element's dimensions performing
9246          * neccessary box adjustments.
9247          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9248          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9249          * @return {Roo.Element} this
9250          */
9251         fitToParent : function(monitorResize, targetParent) {
9252           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9253           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9254           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9255             return;
9256           }
9257           var p = Roo.get(targetParent || this.dom.parentNode);
9258           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9259           if (monitorResize === true) {
9260             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9261             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9262           }
9263           return this;
9264         },
9265
9266         /**
9267          * Gets the next sibling, skipping text nodes
9268          * @return {HTMLElement} The next sibling or null
9269          */
9270         getNextSibling : function(){
9271             var n = this.dom.nextSibling;
9272             while(n && n.nodeType != 1){
9273                 n = n.nextSibling;
9274             }
9275             return n;
9276         },
9277
9278         /**
9279          * Gets the previous sibling, skipping text nodes
9280          * @return {HTMLElement} The previous sibling or null
9281          */
9282         getPrevSibling : function(){
9283             var n = this.dom.previousSibling;
9284             while(n && n.nodeType != 1){
9285                 n = n.previousSibling;
9286             }
9287             return n;
9288         },
9289
9290
9291         /**
9292          * Appends the passed element(s) to this element
9293          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9294          * @return {Roo.Element} this
9295          */
9296         appendChild: function(el){
9297             el = Roo.get(el);
9298             el.appendTo(this);
9299             return this;
9300         },
9301
9302         /**
9303          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9304          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9305          * automatically generated with the specified attributes.
9306          * @param {HTMLElement} insertBefore (optional) a child element of this element
9307          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9308          * @return {Roo.Element} The new child element
9309          */
9310         createChild: function(config, insertBefore, returnDom){
9311             config = config || {tag:'div'};
9312             if(insertBefore){
9313                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9314             }
9315             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9316         },
9317
9318         /**
9319          * Appends this element to the passed element
9320          * @param {String/HTMLElement/Element} el The new parent element
9321          * @return {Roo.Element} this
9322          */
9323         appendTo: function(el){
9324             el = Roo.getDom(el);
9325             el.appendChild(this.dom);
9326             return this;
9327         },
9328
9329         /**
9330          * Inserts this element before the passed element in the DOM
9331          * @param {String/HTMLElement/Element} el The element to insert before
9332          * @return {Roo.Element} this
9333          */
9334         insertBefore: function(el){
9335             el = Roo.getDom(el);
9336             el.parentNode.insertBefore(this.dom, el);
9337             return this;
9338         },
9339
9340         /**
9341          * Inserts this element after the passed element in the DOM
9342          * @param {String/HTMLElement/Element} el The element to insert after
9343          * @return {Roo.Element} this
9344          */
9345         insertAfter: function(el){
9346             el = Roo.getDom(el);
9347             el.parentNode.insertBefore(this.dom, el.nextSibling);
9348             return this;
9349         },
9350
9351         /**
9352          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9353          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9354          * @return {Roo.Element} The new child
9355          */
9356         insertFirst: function(el, returnDom){
9357             el = el || {};
9358             if(typeof el == 'object' && !el.nodeType){ // dh config
9359                 return this.createChild(el, this.dom.firstChild, returnDom);
9360             }else{
9361                 el = Roo.getDom(el);
9362                 this.dom.insertBefore(el, this.dom.firstChild);
9363                 return !returnDom ? Roo.get(el) : el;
9364             }
9365         },
9366
9367         /**
9368          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9369          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9370          * @param {String} where (optional) 'before' or 'after' defaults to before
9371          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9372          * @return {Roo.Element} the inserted Element
9373          */
9374         insertSibling: function(el, where, returnDom){
9375             where = where ? where.toLowerCase() : 'before';
9376             el = el || {};
9377             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9378
9379             if(typeof el == 'object' && !el.nodeType){ // dh config
9380                 if(where == 'after' && !this.dom.nextSibling){
9381                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9382                 }else{
9383                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9384                 }
9385
9386             }else{
9387                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9388                             where == 'before' ? this.dom : this.dom.nextSibling);
9389                 if(!returnDom){
9390                     rt = Roo.get(rt);
9391                 }
9392             }
9393             return rt;
9394         },
9395
9396         /**
9397          * Creates and wraps this element with another element
9398          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9399          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9400          * @return {HTMLElement/Element} The newly created wrapper element
9401          */
9402         wrap: function(config, returnDom){
9403             if(!config){
9404                 config = {tag: "div"};
9405             }
9406             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9407             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9408             return newEl;
9409         },
9410
9411         /**
9412          * Replaces the passed element with this element
9413          * @param {String/HTMLElement/Element} el The element to replace
9414          * @return {Roo.Element} this
9415          */
9416         replace: function(el){
9417             el = Roo.get(el);
9418             this.insertBefore(el);
9419             el.remove();
9420             return this;
9421         },
9422
9423         /**
9424          * Inserts an html fragment into this element
9425          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9426          * @param {String} html The HTML fragment
9427          * @param {Boolean} returnEl True to return an Roo.Element
9428          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9429          */
9430         insertHtml : function(where, html, returnEl){
9431             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9432             return returnEl ? Roo.get(el) : el;
9433         },
9434
9435         /**
9436          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9437          * @param {Object} o The object with the attributes
9438          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9439          * @return {Roo.Element} this
9440          */
9441         set : function(o, useSet){
9442             var el = this.dom;
9443             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9444             for(var attr in o){
9445                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9446                 if(attr=="cls"){
9447                     el.className = o["cls"];
9448                 }else{
9449                     if(useSet) {
9450                         el.setAttribute(attr, o[attr]);
9451                     } else {
9452                         el[attr] = o[attr];
9453                     }
9454                 }
9455             }
9456             if(o.style){
9457                 Roo.DomHelper.applyStyles(el, o.style);
9458             }
9459             return this;
9460         },
9461
9462         /**
9463          * Convenience method for constructing a KeyMap
9464          * @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:
9465          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9466          * @param {Function} fn The function to call
9467          * @param {Object} scope (optional) The scope of the function
9468          * @return {Roo.KeyMap} The KeyMap created
9469          */
9470         addKeyListener : function(key, fn, scope){
9471             var config;
9472             if(typeof key != "object" || key instanceof Array){
9473                 config = {
9474                     key: key,
9475                     fn: fn,
9476                     scope: scope
9477                 };
9478             }else{
9479                 config = {
9480                     key : key.key,
9481                     shift : key.shift,
9482                     ctrl : key.ctrl,
9483                     alt : key.alt,
9484                     fn: fn,
9485                     scope: scope
9486                 };
9487             }
9488             return new Roo.KeyMap(this, config);
9489         },
9490
9491         /**
9492          * Creates a KeyMap for this element
9493          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9494          * @return {Roo.KeyMap} The KeyMap created
9495          */
9496         addKeyMap : function(config){
9497             return new Roo.KeyMap(this, config);
9498         },
9499
9500         /**
9501          * Returns true if this element is scrollable.
9502          * @return {Boolean}
9503          */
9504          isScrollable : function(){
9505             var dom = this.dom;
9506             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9507         },
9508
9509         /**
9510          * 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().
9511          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9512          * @param {Number} value The new scroll value
9513          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9514          * @return {Element} this
9515          */
9516
9517         scrollTo : function(side, value, animate){
9518             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9519             if(!animate || !A){
9520                 this.dom[prop] = value;
9521             }else{
9522                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9523                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9524             }
9525             return this;
9526         },
9527
9528         /**
9529          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9530          * within this element's scrollable range.
9531          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9532          * @param {Number} distance How far to scroll the element in pixels
9533          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9534          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9535          * was scrolled as far as it could go.
9536          */
9537          scroll : function(direction, distance, animate){
9538              if(!this.isScrollable()){
9539                  return;
9540              }
9541              var el = this.dom;
9542              var l = el.scrollLeft, t = el.scrollTop;
9543              var w = el.scrollWidth, h = el.scrollHeight;
9544              var cw = el.clientWidth, ch = el.clientHeight;
9545              direction = direction.toLowerCase();
9546              var scrolled = false;
9547              var a = this.preanim(arguments, 2);
9548              switch(direction){
9549                  case "l":
9550                  case "left":
9551                      if(w - l > cw){
9552                          var v = Math.min(l + distance, w-cw);
9553                          this.scrollTo("left", v, a);
9554                          scrolled = true;
9555                      }
9556                      break;
9557                 case "r":
9558                 case "right":
9559                      if(l > 0){
9560                          var v = Math.max(l - distance, 0);
9561                          this.scrollTo("left", v, a);
9562                          scrolled = true;
9563                      }
9564                      break;
9565                 case "t":
9566                 case "top":
9567                 case "up":
9568                      if(t > 0){
9569                          var v = Math.max(t - distance, 0);
9570                          this.scrollTo("top", v, a);
9571                          scrolled = true;
9572                      }
9573                      break;
9574                 case "b":
9575                 case "bottom":
9576                 case "down":
9577                      if(h - t > ch){
9578                          var v = Math.min(t + distance, h-ch);
9579                          this.scrollTo("top", v, a);
9580                          scrolled = true;
9581                      }
9582                      break;
9583              }
9584              return scrolled;
9585         },
9586
9587         /**
9588          * Translates the passed page coordinates into left/top css values for this element
9589          * @param {Number/Array} x The page x or an array containing [x, y]
9590          * @param {Number} y The page y
9591          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9592          */
9593         translatePoints : function(x, y){
9594             if(typeof x == 'object' || x instanceof Array){
9595                 y = x[1]; x = x[0];
9596             }
9597             var p = this.getStyle('position');
9598             var o = this.getXY();
9599
9600             var l = parseInt(this.getStyle('left'), 10);
9601             var t = parseInt(this.getStyle('top'), 10);
9602
9603             if(isNaN(l)){
9604                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9605             }
9606             if(isNaN(t)){
9607                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9608             }
9609
9610             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9611         },
9612
9613         /**
9614          * Returns the current scroll position of the element.
9615          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9616          */
9617         getScroll : function(){
9618             var d = this.dom, doc = document;
9619             if(d == doc || d == doc.body){
9620                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9621                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9622                 return {left: l, top: t};
9623             }else{
9624                 return {left: d.scrollLeft, top: d.scrollTop};
9625             }
9626         },
9627
9628         /**
9629          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9630          * are convert to standard 6 digit hex color.
9631          * @param {String} attr The css attribute
9632          * @param {String} defaultValue The default value to use when a valid color isn't found
9633          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9634          * YUI color anims.
9635          */
9636         getColor : function(attr, defaultValue, prefix){
9637             var v = this.getStyle(attr);
9638             if(!v || v == "transparent" || v == "inherit") {
9639                 return defaultValue;
9640             }
9641             var color = typeof prefix == "undefined" ? "#" : prefix;
9642             if(v.substr(0, 4) == "rgb("){
9643                 var rvs = v.slice(4, v.length -1).split(",");
9644                 for(var i = 0; i < 3; i++){
9645                     var h = parseInt(rvs[i]).toString(16);
9646                     if(h < 16){
9647                         h = "0" + h;
9648                     }
9649                     color += h;
9650                 }
9651             } else {
9652                 if(v.substr(0, 1) == "#"){
9653                     if(v.length == 4) {
9654                         for(var i = 1; i < 4; i++){
9655                             var c = v.charAt(i);
9656                             color +=  c + c;
9657                         }
9658                     }else if(v.length == 7){
9659                         color += v.substr(1);
9660                     }
9661                 }
9662             }
9663             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9664         },
9665
9666         /**
9667          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9668          * gradient background, rounded corners and a 4-way shadow.
9669          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9670          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9671          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9672          * @return {Roo.Element} this
9673          */
9674         boxWrap : function(cls){
9675             cls = cls || 'x-box';
9676             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9677             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9678             return el;
9679         },
9680
9681         /**
9682          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9683          * @param {String} namespace The namespace in which to look for the attribute
9684          * @param {String} name The attribute name
9685          * @return {String} The attribute value
9686          */
9687         getAttributeNS : Roo.isIE ? function(ns, name){
9688             var d = this.dom;
9689             var type = typeof d[ns+":"+name];
9690             if(type != 'undefined' && type != 'unknown'){
9691                 return d[ns+":"+name];
9692             }
9693             return d[name];
9694         } : function(ns, name){
9695             var d = this.dom;
9696             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9697         },
9698         
9699         
9700         /**
9701          * Sets or Returns the value the dom attribute value
9702          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9703          * @param {String} value (optional) The value to set the attribute to
9704          * @return {String} The attribute value
9705          */
9706         attr : function(name){
9707             if (arguments.length > 1) {
9708                 this.dom.setAttribute(name, arguments[1]);
9709                 return arguments[1];
9710             }
9711             if (typeof(name) == 'object') {
9712                 for(var i in name) {
9713                     this.attr(i, name[i]);
9714                 }
9715                 return name;
9716             }
9717             
9718             
9719             if (!this.dom.hasAttribute(name)) {
9720                 return undefined;
9721             }
9722             return this.dom.getAttribute(name);
9723         }
9724         
9725         
9726         
9727     };
9728
9729     var ep = El.prototype;
9730
9731     /**
9732      * Appends an event handler (Shorthand for addListener)
9733      * @param {String}   eventName     The type of event to append
9734      * @param {Function} fn        The method the event invokes
9735      * @param {Object} scope       (optional) The scope (this object) of the fn
9736      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9737      * @method
9738      */
9739     ep.on = ep.addListener;
9740         // backwards compat
9741     ep.mon = ep.addListener;
9742
9743     /**
9744      * Removes an event handler from this element (shorthand for removeListener)
9745      * @param {String} eventName the type of event to remove
9746      * @param {Function} fn the method the event invokes
9747      * @return {Roo.Element} this
9748      * @method
9749      */
9750     ep.un = ep.removeListener;
9751
9752     /**
9753      * true to automatically adjust width and height settings for box-model issues (default to true)
9754      */
9755     ep.autoBoxAdjust = true;
9756
9757     // private
9758     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9759
9760     // private
9761     El.addUnits = function(v, defaultUnit){
9762         if(v === "" || v == "auto"){
9763             return v;
9764         }
9765         if(v === undefined){
9766             return '';
9767         }
9768         if(typeof v == "number" || !El.unitPattern.test(v)){
9769             return v + (defaultUnit || 'px');
9770         }
9771         return v;
9772     };
9773
9774     // special markup used throughout Roo when box wrapping elements
9775     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>';
9776     /**
9777      * Visibility mode constant - Use visibility to hide element
9778      * @static
9779      * @type Number
9780      */
9781     El.VISIBILITY = 1;
9782     /**
9783      * Visibility mode constant - Use display to hide element
9784      * @static
9785      * @type Number
9786      */
9787     El.DISPLAY = 2;
9788
9789     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9790     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9791     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9792
9793
9794
9795     /**
9796      * @private
9797      */
9798     El.cache = {};
9799
9800     var docEl;
9801
9802     /**
9803      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9804      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9805      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9806      * @return {Element} The Element object
9807      * @static
9808      */
9809     El.get = function(el){
9810         var ex, elm, id;
9811         if(!el){ return null; }
9812         if(typeof el == "string"){ // element id
9813             if(!(elm = document.getElementById(el))){
9814                 return null;
9815             }
9816             if(ex = El.cache[el]){
9817                 ex.dom = elm;
9818             }else{
9819                 ex = El.cache[el] = new El(elm);
9820             }
9821             return ex;
9822         }else if(el.tagName){ // dom element
9823             if(!(id = el.id)){
9824                 id = Roo.id(el);
9825             }
9826             if(ex = El.cache[id]){
9827                 ex.dom = el;
9828             }else{
9829                 ex = El.cache[id] = new El(el);
9830             }
9831             return ex;
9832         }else if(el instanceof El){
9833             if(el != docEl){
9834                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9835                                                               // catch case where it hasn't been appended
9836                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9837             }
9838             return el;
9839         }else if(el.isComposite){
9840             return el;
9841         }else if(el instanceof Array){
9842             return El.select(el);
9843         }else if(el == document){
9844             // create a bogus element object representing the document object
9845             if(!docEl){
9846                 var f = function(){};
9847                 f.prototype = El.prototype;
9848                 docEl = new f();
9849                 docEl.dom = document;
9850             }
9851             return docEl;
9852         }
9853         return null;
9854     };
9855
9856     // private
9857     El.uncache = function(el){
9858         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9859             if(a[i]){
9860                 delete El.cache[a[i].id || a[i]];
9861             }
9862         }
9863     };
9864
9865     // private
9866     // Garbage collection - uncache elements/purge listeners on orphaned elements
9867     // so we don't hold a reference and cause the browser to retain them
9868     El.garbageCollect = function(){
9869         if(!Roo.enableGarbageCollector){
9870             clearInterval(El.collectorThread);
9871             return;
9872         }
9873         for(var eid in El.cache){
9874             var el = El.cache[eid], d = el.dom;
9875             // -------------------------------------------------------
9876             // Determining what is garbage:
9877             // -------------------------------------------------------
9878             // !d
9879             // dom node is null, definitely garbage
9880             // -------------------------------------------------------
9881             // !d.parentNode
9882             // no parentNode == direct orphan, definitely garbage
9883             // -------------------------------------------------------
9884             // !d.offsetParent && !document.getElementById(eid)
9885             // display none elements have no offsetParent so we will
9886             // also try to look it up by it's id. However, check
9887             // offsetParent first so we don't do unneeded lookups.
9888             // This enables collection of elements that are not orphans
9889             // directly, but somewhere up the line they have an orphan
9890             // parent.
9891             // -------------------------------------------------------
9892             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9893                 delete El.cache[eid];
9894                 if(d && Roo.enableListenerCollection){
9895                     E.purgeElement(d);
9896                 }
9897             }
9898         }
9899     }
9900     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9901
9902
9903     // dom is optional
9904     El.Flyweight = function(dom){
9905         this.dom = dom;
9906     };
9907     El.Flyweight.prototype = El.prototype;
9908
9909     El._flyweights = {};
9910     /**
9911      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9912      * the dom node can be overwritten by other code.
9913      * @param {String/HTMLElement} el The dom node or id
9914      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9915      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9916      * @static
9917      * @return {Element} The shared Element object
9918      */
9919     El.fly = function(el, named){
9920         named = named || '_global';
9921         el = Roo.getDom(el);
9922         if(!el){
9923             return null;
9924         }
9925         if(!El._flyweights[named]){
9926             El._flyweights[named] = new El.Flyweight();
9927         }
9928         El._flyweights[named].dom = el;
9929         return El._flyweights[named];
9930     };
9931
9932     /**
9933      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9934      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9935      * Shorthand of {@link Roo.Element#get}
9936      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9937      * @return {Element} The Element object
9938      * @member Roo
9939      * @method get
9940      */
9941     Roo.get = El.get;
9942     /**
9943      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9944      * the dom node can be overwritten by other code.
9945      * Shorthand of {@link Roo.Element#fly}
9946      * @param {String/HTMLElement} el The dom node or id
9947      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9948      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9949      * @static
9950      * @return {Element} The shared Element object
9951      * @member Roo
9952      * @method fly
9953      */
9954     Roo.fly = El.fly;
9955
9956     // speedy lookup for elements never to box adjust
9957     var noBoxAdjust = Roo.isStrict ? {
9958         select:1
9959     } : {
9960         input:1, select:1, textarea:1
9961     };
9962     if(Roo.isIE || Roo.isGecko){
9963         noBoxAdjust['button'] = 1;
9964     }
9965
9966
9967     Roo.EventManager.on(window, 'unload', function(){
9968         delete El.cache;
9969         delete El._flyweights;
9970     });
9971 })();
9972
9973
9974
9975
9976 if(Roo.DomQuery){
9977     Roo.Element.selectorFunction = Roo.DomQuery.select;
9978 }
9979
9980 Roo.Element.select = function(selector, unique, root){
9981     var els;
9982     if(typeof selector == "string"){
9983         els = Roo.Element.selectorFunction(selector, root);
9984     }else if(selector.length !== undefined){
9985         els = selector;
9986     }else{
9987         throw "Invalid selector";
9988     }
9989     if(unique === true){
9990         return new Roo.CompositeElement(els);
9991     }else{
9992         return new Roo.CompositeElementLite(els);
9993     }
9994 };
9995 /**
9996  * Selects elements based on the passed CSS selector to enable working on them as 1.
9997  * @param {String/Array} selector The CSS selector or an array of elements
9998  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9999  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10000  * @return {CompositeElementLite/CompositeElement}
10001  * @member Roo
10002  * @method select
10003  */
10004 Roo.select = Roo.Element.select;
10005
10006
10007
10008
10009
10010
10011
10012
10013
10014
10015
10016
10017
10018
10019 /*
10020  * Based on:
10021  * Ext JS Library 1.1.1
10022  * Copyright(c) 2006-2007, Ext JS, LLC.
10023  *
10024  * Originally Released Under LGPL - original licence link has changed is not relivant.
10025  *
10026  * Fork - LGPL
10027  * <script type="text/javascript">
10028  */
10029
10030
10031
10032 //Notifies Element that fx methods are available
10033 Roo.enableFx = true;
10034
10035 /**
10036  * @class Roo.Fx
10037  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10038  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10039  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10040  * Element effects to work.</p><br/>
10041  *
10042  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10043  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10044  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10045  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10046  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10047  * expected results and should be done with care.</p><br/>
10048  *
10049  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10050  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10051 <pre>
10052 Value  Description
10053 -----  -----------------------------
10054 tl     The top left corner
10055 t      The center of the top edge
10056 tr     The top right corner
10057 l      The center of the left edge
10058 r      The center of the right edge
10059 bl     The bottom left corner
10060 b      The center of the bottom edge
10061 br     The bottom right corner
10062 </pre>
10063  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10064  * below are common options that can be passed to any Fx method.</b>
10065  * @cfg {Function} callback A function called when the effect is finished
10066  * @cfg {Object} scope The scope of the effect function
10067  * @cfg {String} easing A valid Easing value for the effect
10068  * @cfg {String} afterCls A css class to apply after the effect
10069  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10070  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10071  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10072  * effects that end with the element being visually hidden, ignored otherwise)
10073  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10074  * a function which returns such a specification that will be applied to the Element after the effect finishes
10075  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10076  * @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
10077  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10078  */
10079 Roo.Fx = {
10080         /**
10081          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10082          * origin for the slide effect.  This function automatically handles wrapping the element with
10083          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10084          * Usage:
10085          *<pre><code>
10086 // default: slide the element in from the top
10087 el.slideIn();
10088
10089 // custom: slide the element in from the right with a 2-second duration
10090 el.slideIn('r', { duration: 2 });
10091
10092 // common config options shown with default values
10093 el.slideIn('t', {
10094     easing: 'easeOut',
10095     duration: .5
10096 });
10097 </code></pre>
10098          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10099          * @param {Object} options (optional) Object literal with any of the Fx config options
10100          * @return {Roo.Element} The Element
10101          */
10102     slideIn : function(anchor, o){
10103         var el = this.getFxEl();
10104         o = o || {};
10105
10106         el.queueFx(o, function(){
10107
10108             anchor = anchor || "t";
10109
10110             // fix display to visibility
10111             this.fixDisplay();
10112
10113             // restore values after effect
10114             var r = this.getFxRestore();
10115             var b = this.getBox();
10116             // fixed size for slide
10117             this.setSize(b);
10118
10119             // wrap if needed
10120             var wrap = this.fxWrap(r.pos, o, "hidden");
10121
10122             var st = this.dom.style;
10123             st.visibility = "visible";
10124             st.position = "absolute";
10125
10126             // clear out temp styles after slide and unwrap
10127             var after = function(){
10128                 el.fxUnwrap(wrap, r.pos, o);
10129                 st.width = r.width;
10130                 st.height = r.height;
10131                 el.afterFx(o);
10132             };
10133             // time to calc the positions
10134             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10135
10136             switch(anchor.toLowerCase()){
10137                 case "t":
10138                     wrap.setSize(b.width, 0);
10139                     st.left = st.bottom = "0";
10140                     a = {height: bh};
10141                 break;
10142                 case "l":
10143                     wrap.setSize(0, b.height);
10144                     st.right = st.top = "0";
10145                     a = {width: bw};
10146                 break;
10147                 case "r":
10148                     wrap.setSize(0, b.height);
10149                     wrap.setX(b.right);
10150                     st.left = st.top = "0";
10151                     a = {width: bw, points: pt};
10152                 break;
10153                 case "b":
10154                     wrap.setSize(b.width, 0);
10155                     wrap.setY(b.bottom);
10156                     st.left = st.top = "0";
10157                     a = {height: bh, points: pt};
10158                 break;
10159                 case "tl":
10160                     wrap.setSize(0, 0);
10161                     st.right = st.bottom = "0";
10162                     a = {width: bw, height: bh};
10163                 break;
10164                 case "bl":
10165                     wrap.setSize(0, 0);
10166                     wrap.setY(b.y+b.height);
10167                     st.right = st.top = "0";
10168                     a = {width: bw, height: bh, points: pt};
10169                 break;
10170                 case "br":
10171                     wrap.setSize(0, 0);
10172                     wrap.setXY([b.right, b.bottom]);
10173                     st.left = st.top = "0";
10174                     a = {width: bw, height: bh, points: pt};
10175                 break;
10176                 case "tr":
10177                     wrap.setSize(0, 0);
10178                     wrap.setX(b.x+b.width);
10179                     st.left = st.bottom = "0";
10180                     a = {width: bw, height: bh, points: pt};
10181                 break;
10182             }
10183             this.dom.style.visibility = "visible";
10184             wrap.show();
10185
10186             arguments.callee.anim = wrap.fxanim(a,
10187                 o,
10188                 'motion',
10189                 .5,
10190                 'easeOut', after);
10191         });
10192         return this;
10193     },
10194     
10195         /**
10196          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10197          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10198          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10199          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10200          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10201          * Usage:
10202          *<pre><code>
10203 // default: slide the element out to the top
10204 el.slideOut();
10205
10206 // custom: slide the element out to the right with a 2-second duration
10207 el.slideOut('r', { duration: 2 });
10208
10209 // common config options shown with default values
10210 el.slideOut('t', {
10211     easing: 'easeOut',
10212     duration: .5,
10213     remove: false,
10214     useDisplay: false
10215 });
10216 </code></pre>
10217          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10218          * @param {Object} options (optional) Object literal with any of the Fx config options
10219          * @return {Roo.Element} The Element
10220          */
10221     slideOut : function(anchor, o){
10222         var el = this.getFxEl();
10223         o = o || {};
10224
10225         el.queueFx(o, function(){
10226
10227             anchor = anchor || "t";
10228
10229             // restore values after effect
10230             var r = this.getFxRestore();
10231             
10232             var b = this.getBox();
10233             // fixed size for slide
10234             this.setSize(b);
10235
10236             // wrap if needed
10237             var wrap = this.fxWrap(r.pos, o, "visible");
10238
10239             var st = this.dom.style;
10240             st.visibility = "visible";
10241             st.position = "absolute";
10242
10243             wrap.setSize(b);
10244
10245             var after = function(){
10246                 if(o.useDisplay){
10247                     el.setDisplayed(false);
10248                 }else{
10249                     el.hide();
10250                 }
10251
10252                 el.fxUnwrap(wrap, r.pos, o);
10253
10254                 st.width = r.width;
10255                 st.height = r.height;
10256
10257                 el.afterFx(o);
10258             };
10259
10260             var a, zero = {to: 0};
10261             switch(anchor.toLowerCase()){
10262                 case "t":
10263                     st.left = st.bottom = "0";
10264                     a = {height: zero};
10265                 break;
10266                 case "l":
10267                     st.right = st.top = "0";
10268                     a = {width: zero};
10269                 break;
10270                 case "r":
10271                     st.left = st.top = "0";
10272                     a = {width: zero, points: {to:[b.right, b.y]}};
10273                 break;
10274                 case "b":
10275                     st.left = st.top = "0";
10276                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10277                 break;
10278                 case "tl":
10279                     st.right = st.bottom = "0";
10280                     a = {width: zero, height: zero};
10281                 break;
10282                 case "bl":
10283                     st.right = st.top = "0";
10284                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10285                 break;
10286                 case "br":
10287                     st.left = st.top = "0";
10288                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10289                 break;
10290                 case "tr":
10291                     st.left = st.bottom = "0";
10292                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10293                 break;
10294             }
10295
10296             arguments.callee.anim = wrap.fxanim(a,
10297                 o,
10298                 'motion',
10299                 .5,
10300                 "easeOut", after);
10301         });
10302         return this;
10303     },
10304
10305         /**
10306          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10307          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10308          * The element must be removed from the DOM using the 'remove' config option if desired.
10309          * Usage:
10310          *<pre><code>
10311 // default
10312 el.puff();
10313
10314 // common config options shown with default values
10315 el.puff({
10316     easing: 'easeOut',
10317     duration: .5,
10318     remove: false,
10319     useDisplay: false
10320 });
10321 </code></pre>
10322          * @param {Object} options (optional) Object literal with any of the Fx config options
10323          * @return {Roo.Element} The Element
10324          */
10325     puff : function(o){
10326         var el = this.getFxEl();
10327         o = o || {};
10328
10329         el.queueFx(o, function(){
10330             this.clearOpacity();
10331             this.show();
10332
10333             // restore values after effect
10334             var r = this.getFxRestore();
10335             var st = this.dom.style;
10336
10337             var after = function(){
10338                 if(o.useDisplay){
10339                     el.setDisplayed(false);
10340                 }else{
10341                     el.hide();
10342                 }
10343
10344                 el.clearOpacity();
10345
10346                 el.setPositioning(r.pos);
10347                 st.width = r.width;
10348                 st.height = r.height;
10349                 st.fontSize = '';
10350                 el.afterFx(o);
10351             };
10352
10353             var width = this.getWidth();
10354             var height = this.getHeight();
10355
10356             arguments.callee.anim = this.fxanim({
10357                     width : {to: this.adjustWidth(width * 2)},
10358                     height : {to: this.adjustHeight(height * 2)},
10359                     points : {by: [-(width * .5), -(height * .5)]},
10360                     opacity : {to: 0},
10361                     fontSize: {to:200, unit: "%"}
10362                 },
10363                 o,
10364                 'motion',
10365                 .5,
10366                 "easeOut", after);
10367         });
10368         return this;
10369     },
10370
10371         /**
10372          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10373          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10374          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10375          * Usage:
10376          *<pre><code>
10377 // default
10378 el.switchOff();
10379
10380 // all config options shown with default values
10381 el.switchOff({
10382     easing: 'easeIn',
10383     duration: .3,
10384     remove: false,
10385     useDisplay: false
10386 });
10387 </code></pre>
10388          * @param {Object} options (optional) Object literal with any of the Fx config options
10389          * @return {Roo.Element} The Element
10390          */
10391     switchOff : function(o){
10392         var el = this.getFxEl();
10393         o = o || {};
10394
10395         el.queueFx(o, function(){
10396             this.clearOpacity();
10397             this.clip();
10398
10399             // restore values after effect
10400             var r = this.getFxRestore();
10401             var st = this.dom.style;
10402
10403             var after = function(){
10404                 if(o.useDisplay){
10405                     el.setDisplayed(false);
10406                 }else{
10407                     el.hide();
10408                 }
10409
10410                 el.clearOpacity();
10411                 el.setPositioning(r.pos);
10412                 st.width = r.width;
10413                 st.height = r.height;
10414
10415                 el.afterFx(o);
10416             };
10417
10418             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10419                 this.clearOpacity();
10420                 (function(){
10421                     this.fxanim({
10422                         height:{to:1},
10423                         points:{by:[0, this.getHeight() * .5]}
10424                     }, o, 'motion', 0.3, 'easeIn', after);
10425                 }).defer(100, this);
10426             });
10427         });
10428         return this;
10429     },
10430
10431     /**
10432      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10433      * changed using the "attr" config option) and then fading back to the original color. If no original
10434      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10435      * Usage:
10436 <pre><code>
10437 // default: highlight background to yellow
10438 el.highlight();
10439
10440 // custom: highlight foreground text to blue for 2 seconds
10441 el.highlight("0000ff", { attr: 'color', duration: 2 });
10442
10443 // common config options shown with default values
10444 el.highlight("ffff9c", {
10445     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10446     endColor: (current color) or "ffffff",
10447     easing: 'easeIn',
10448     duration: 1
10449 });
10450 </code></pre>
10451      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10452      * @param {Object} options (optional) Object literal with any of the Fx config options
10453      * @return {Roo.Element} The Element
10454      */ 
10455     highlight : function(color, o){
10456         var el = this.getFxEl();
10457         o = o || {};
10458
10459         el.queueFx(o, function(){
10460             color = color || "ffff9c";
10461             attr = o.attr || "backgroundColor";
10462
10463             this.clearOpacity();
10464             this.show();
10465
10466             var origColor = this.getColor(attr);
10467             var restoreColor = this.dom.style[attr];
10468             endColor = (o.endColor || origColor) || "ffffff";
10469
10470             var after = function(){
10471                 el.dom.style[attr] = restoreColor;
10472                 el.afterFx(o);
10473             };
10474
10475             var a = {};
10476             a[attr] = {from: color, to: endColor};
10477             arguments.callee.anim = this.fxanim(a,
10478                 o,
10479                 'color',
10480                 1,
10481                 'easeIn', after);
10482         });
10483         return this;
10484     },
10485
10486    /**
10487     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10488     * Usage:
10489 <pre><code>
10490 // default: a single light blue ripple
10491 el.frame();
10492
10493 // custom: 3 red ripples lasting 3 seconds total
10494 el.frame("ff0000", 3, { duration: 3 });
10495
10496 // common config options shown with default values
10497 el.frame("C3DAF9", 1, {
10498     duration: 1 //duration of entire animation (not each individual ripple)
10499     // Note: Easing is not configurable and will be ignored if included
10500 });
10501 </code></pre>
10502     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10503     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10504     * @param {Object} options (optional) Object literal with any of the Fx config options
10505     * @return {Roo.Element} The Element
10506     */
10507     frame : function(color, count, o){
10508         var el = this.getFxEl();
10509         o = o || {};
10510
10511         el.queueFx(o, function(){
10512             color = color || "#C3DAF9";
10513             if(color.length == 6){
10514                 color = "#" + color;
10515             }
10516             count = count || 1;
10517             duration = o.duration || 1;
10518             this.show();
10519
10520             var b = this.getBox();
10521             var animFn = function(){
10522                 var proxy = this.createProxy({
10523
10524                      style:{
10525                         visbility:"hidden",
10526                         position:"absolute",
10527                         "z-index":"35000", // yee haw
10528                         border:"0px solid " + color
10529                      }
10530                   });
10531                 var scale = Roo.isBorderBox ? 2 : 1;
10532                 proxy.animate({
10533                     top:{from:b.y, to:b.y - 20},
10534                     left:{from:b.x, to:b.x - 20},
10535                     borderWidth:{from:0, to:10},
10536                     opacity:{from:1, to:0},
10537                     height:{from:b.height, to:(b.height + (20*scale))},
10538                     width:{from:b.width, to:(b.width + (20*scale))}
10539                 }, duration, function(){
10540                     proxy.remove();
10541                 });
10542                 if(--count > 0){
10543                      animFn.defer((duration/2)*1000, this);
10544                 }else{
10545                     el.afterFx(o);
10546                 }
10547             };
10548             animFn.call(this);
10549         });
10550         return this;
10551     },
10552
10553    /**
10554     * Creates a pause before any subsequent queued effects begin.  If there are
10555     * no effects queued after the pause it will have no effect.
10556     * Usage:
10557 <pre><code>
10558 el.pause(1);
10559 </code></pre>
10560     * @param {Number} seconds The length of time to pause (in seconds)
10561     * @return {Roo.Element} The Element
10562     */
10563     pause : function(seconds){
10564         var el = this.getFxEl();
10565         var o = {};
10566
10567         el.queueFx(o, function(){
10568             setTimeout(function(){
10569                 el.afterFx(o);
10570             }, seconds * 1000);
10571         });
10572         return this;
10573     },
10574
10575    /**
10576     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10577     * using the "endOpacity" config option.
10578     * Usage:
10579 <pre><code>
10580 // default: fade in from opacity 0 to 100%
10581 el.fadeIn();
10582
10583 // custom: fade in from opacity 0 to 75% over 2 seconds
10584 el.fadeIn({ endOpacity: .75, duration: 2});
10585
10586 // common config options shown with default values
10587 el.fadeIn({
10588     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10589     easing: 'easeOut',
10590     duration: .5
10591 });
10592 </code></pre>
10593     * @param {Object} options (optional) Object literal with any of the Fx config options
10594     * @return {Roo.Element} The Element
10595     */
10596     fadeIn : function(o){
10597         var el = this.getFxEl();
10598         o = o || {};
10599         el.queueFx(o, function(){
10600             this.setOpacity(0);
10601             this.fixDisplay();
10602             this.dom.style.visibility = 'visible';
10603             var to = o.endOpacity || 1;
10604             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10605                 o, null, .5, "easeOut", function(){
10606                 if(to == 1){
10607                     this.clearOpacity();
10608                 }
10609                 el.afterFx(o);
10610             });
10611         });
10612         return this;
10613     },
10614
10615    /**
10616     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10617     * using the "endOpacity" config option.
10618     * Usage:
10619 <pre><code>
10620 // default: fade out from the element's current opacity to 0
10621 el.fadeOut();
10622
10623 // custom: fade out from the element's current opacity to 25% over 2 seconds
10624 el.fadeOut({ endOpacity: .25, duration: 2});
10625
10626 // common config options shown with default values
10627 el.fadeOut({
10628     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10629     easing: 'easeOut',
10630     duration: .5
10631     remove: false,
10632     useDisplay: false
10633 });
10634 </code></pre>
10635     * @param {Object} options (optional) Object literal with any of the Fx config options
10636     * @return {Roo.Element} The Element
10637     */
10638     fadeOut : function(o){
10639         var el = this.getFxEl();
10640         o = o || {};
10641         el.queueFx(o, function(){
10642             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10643                 o, null, .5, "easeOut", function(){
10644                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10645                      this.dom.style.display = "none";
10646                 }else{
10647                      this.dom.style.visibility = "hidden";
10648                 }
10649                 this.clearOpacity();
10650                 el.afterFx(o);
10651             });
10652         });
10653         return this;
10654     },
10655
10656    /**
10657     * Animates the transition of an element's dimensions from a starting height/width
10658     * to an ending height/width.
10659     * Usage:
10660 <pre><code>
10661 // change height and width to 100x100 pixels
10662 el.scale(100, 100);
10663
10664 // common config options shown with default values.  The height and width will default to
10665 // the element's existing values if passed as null.
10666 el.scale(
10667     [element's width],
10668     [element's height], {
10669     easing: 'easeOut',
10670     duration: .35
10671 });
10672 </code></pre>
10673     * @param {Number} width  The new width (pass undefined to keep the original width)
10674     * @param {Number} height  The new height (pass undefined to keep the original height)
10675     * @param {Object} options (optional) Object literal with any of the Fx config options
10676     * @return {Roo.Element} The Element
10677     */
10678     scale : function(w, h, o){
10679         this.shift(Roo.apply({}, o, {
10680             width: w,
10681             height: h
10682         }));
10683         return this;
10684     },
10685
10686    /**
10687     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10688     * Any of these properties not specified in the config object will not be changed.  This effect 
10689     * requires that at least one new dimension, position or opacity setting must be passed in on
10690     * the config object in order for the function to have any effect.
10691     * Usage:
10692 <pre><code>
10693 // slide the element horizontally to x position 200 while changing the height and opacity
10694 el.shift({ x: 200, height: 50, opacity: .8 });
10695
10696 // common config options shown with default values.
10697 el.shift({
10698     width: [element's width],
10699     height: [element's height],
10700     x: [element's x position],
10701     y: [element's y position],
10702     opacity: [element's opacity],
10703     easing: 'easeOut',
10704     duration: .35
10705 });
10706 </code></pre>
10707     * @param {Object} options  Object literal with any of the Fx config options
10708     * @return {Roo.Element} The Element
10709     */
10710     shift : function(o){
10711         var el = this.getFxEl();
10712         o = o || {};
10713         el.queueFx(o, function(){
10714             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10715             if(w !== undefined){
10716                 a.width = {to: this.adjustWidth(w)};
10717             }
10718             if(h !== undefined){
10719                 a.height = {to: this.adjustHeight(h)};
10720             }
10721             if(x !== undefined || y !== undefined){
10722                 a.points = {to: [
10723                     x !== undefined ? x : this.getX(),
10724                     y !== undefined ? y : this.getY()
10725                 ]};
10726             }
10727             if(op !== undefined){
10728                 a.opacity = {to: op};
10729             }
10730             if(o.xy !== undefined){
10731                 a.points = {to: o.xy};
10732             }
10733             arguments.callee.anim = this.fxanim(a,
10734                 o, 'motion', .35, "easeOut", function(){
10735                 el.afterFx(o);
10736             });
10737         });
10738         return this;
10739     },
10740
10741         /**
10742          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10743          * ending point of the effect.
10744          * Usage:
10745          *<pre><code>
10746 // default: slide the element downward while fading out
10747 el.ghost();
10748
10749 // custom: slide the element out to the right with a 2-second duration
10750 el.ghost('r', { duration: 2 });
10751
10752 // common config options shown with default values
10753 el.ghost('b', {
10754     easing: 'easeOut',
10755     duration: .5
10756     remove: false,
10757     useDisplay: false
10758 });
10759 </code></pre>
10760          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10761          * @param {Object} options (optional) Object literal with any of the Fx config options
10762          * @return {Roo.Element} The Element
10763          */
10764     ghost : function(anchor, o){
10765         var el = this.getFxEl();
10766         o = o || {};
10767
10768         el.queueFx(o, function(){
10769             anchor = anchor || "b";
10770
10771             // restore values after effect
10772             var r = this.getFxRestore();
10773             var w = this.getWidth(),
10774                 h = this.getHeight();
10775
10776             var st = this.dom.style;
10777
10778             var after = function(){
10779                 if(o.useDisplay){
10780                     el.setDisplayed(false);
10781                 }else{
10782                     el.hide();
10783                 }
10784
10785                 el.clearOpacity();
10786                 el.setPositioning(r.pos);
10787                 st.width = r.width;
10788                 st.height = r.height;
10789
10790                 el.afterFx(o);
10791             };
10792
10793             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10794             switch(anchor.toLowerCase()){
10795                 case "t":
10796                     pt.by = [0, -h];
10797                 break;
10798                 case "l":
10799                     pt.by = [-w, 0];
10800                 break;
10801                 case "r":
10802                     pt.by = [w, 0];
10803                 break;
10804                 case "b":
10805                     pt.by = [0, h];
10806                 break;
10807                 case "tl":
10808                     pt.by = [-w, -h];
10809                 break;
10810                 case "bl":
10811                     pt.by = [-w, h];
10812                 break;
10813                 case "br":
10814                     pt.by = [w, h];
10815                 break;
10816                 case "tr":
10817                     pt.by = [w, -h];
10818                 break;
10819             }
10820
10821             arguments.callee.anim = this.fxanim(a,
10822                 o,
10823                 'motion',
10824                 .5,
10825                 "easeOut", after);
10826         });
10827         return this;
10828     },
10829
10830         /**
10831          * Ensures that all effects queued after syncFx is called on the element are
10832          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10833          * @return {Roo.Element} The Element
10834          */
10835     syncFx : function(){
10836         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10837             block : false,
10838             concurrent : true,
10839             stopFx : false
10840         });
10841         return this;
10842     },
10843
10844         /**
10845          * Ensures that all effects queued after sequenceFx is called on the element are
10846          * run in sequence.  This is the opposite of {@link #syncFx}.
10847          * @return {Roo.Element} The Element
10848          */
10849     sequenceFx : function(){
10850         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10851             block : false,
10852             concurrent : false,
10853             stopFx : false
10854         });
10855         return this;
10856     },
10857
10858         /* @private */
10859     nextFx : function(){
10860         var ef = this.fxQueue[0];
10861         if(ef){
10862             ef.call(this);
10863         }
10864     },
10865
10866         /**
10867          * Returns true if the element has any effects actively running or queued, else returns false.
10868          * @return {Boolean} True if element has active effects, else false
10869          */
10870     hasActiveFx : function(){
10871         return this.fxQueue && this.fxQueue[0];
10872     },
10873
10874         /**
10875          * Stops any running effects and clears the element's internal effects queue if it contains
10876          * any additional effects that haven't started yet.
10877          * @return {Roo.Element} The Element
10878          */
10879     stopFx : function(){
10880         if(this.hasActiveFx()){
10881             var cur = this.fxQueue[0];
10882             if(cur && cur.anim && cur.anim.isAnimated()){
10883                 this.fxQueue = [cur]; // clear out others
10884                 cur.anim.stop(true);
10885             }
10886         }
10887         return this;
10888     },
10889
10890         /* @private */
10891     beforeFx : function(o){
10892         if(this.hasActiveFx() && !o.concurrent){
10893            if(o.stopFx){
10894                this.stopFx();
10895                return true;
10896            }
10897            return false;
10898         }
10899         return true;
10900     },
10901
10902         /**
10903          * Returns true if the element is currently blocking so that no other effect can be queued
10904          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10905          * used to ensure that an effect initiated by a user action runs to completion prior to the
10906          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10907          * @return {Boolean} True if blocking, else false
10908          */
10909     hasFxBlock : function(){
10910         var q = this.fxQueue;
10911         return q && q[0] && q[0].block;
10912     },
10913
10914         /* @private */
10915     queueFx : function(o, fn){
10916         if(!this.fxQueue){
10917             this.fxQueue = [];
10918         }
10919         if(!this.hasFxBlock()){
10920             Roo.applyIf(o, this.fxDefaults);
10921             if(!o.concurrent){
10922                 var run = this.beforeFx(o);
10923                 fn.block = o.block;
10924                 this.fxQueue.push(fn);
10925                 if(run){
10926                     this.nextFx();
10927                 }
10928             }else{
10929                 fn.call(this);
10930             }
10931         }
10932         return this;
10933     },
10934
10935         /* @private */
10936     fxWrap : function(pos, o, vis){
10937         var wrap;
10938         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10939             var wrapXY;
10940             if(o.fixPosition){
10941                 wrapXY = this.getXY();
10942             }
10943             var div = document.createElement("div");
10944             div.style.visibility = vis;
10945             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10946             wrap.setPositioning(pos);
10947             if(wrap.getStyle("position") == "static"){
10948                 wrap.position("relative");
10949             }
10950             this.clearPositioning('auto');
10951             wrap.clip();
10952             wrap.dom.appendChild(this.dom);
10953             if(wrapXY){
10954                 wrap.setXY(wrapXY);
10955             }
10956         }
10957         return wrap;
10958     },
10959
10960         /* @private */
10961     fxUnwrap : function(wrap, pos, o){
10962         this.clearPositioning();
10963         this.setPositioning(pos);
10964         if(!o.wrap){
10965             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10966             wrap.remove();
10967         }
10968     },
10969
10970         /* @private */
10971     getFxRestore : function(){
10972         var st = this.dom.style;
10973         return {pos: this.getPositioning(), width: st.width, height : st.height};
10974     },
10975
10976         /* @private */
10977     afterFx : function(o){
10978         if(o.afterStyle){
10979             this.applyStyles(o.afterStyle);
10980         }
10981         if(o.afterCls){
10982             this.addClass(o.afterCls);
10983         }
10984         if(o.remove === true){
10985             this.remove();
10986         }
10987         Roo.callback(o.callback, o.scope, [this]);
10988         if(!o.concurrent){
10989             this.fxQueue.shift();
10990             this.nextFx();
10991         }
10992     },
10993
10994         /* @private */
10995     getFxEl : function(){ // support for composite element fx
10996         return Roo.get(this.dom);
10997     },
10998
10999         /* @private */
11000     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11001         animType = animType || 'run';
11002         opt = opt || {};
11003         var anim = Roo.lib.Anim[animType](
11004             this.dom, args,
11005             (opt.duration || defaultDur) || .35,
11006             (opt.easing || defaultEase) || 'easeOut',
11007             function(){
11008                 Roo.callback(cb, this);
11009             },
11010             this
11011         );
11012         opt.anim = anim;
11013         return anim;
11014     }
11015 };
11016
11017 // backwords compat
11018 Roo.Fx.resize = Roo.Fx.scale;
11019
11020 //When included, Roo.Fx is automatically applied to Element so that all basic
11021 //effects are available directly via the Element API
11022 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11023  * Based on:
11024  * Ext JS Library 1.1.1
11025  * Copyright(c) 2006-2007, Ext JS, LLC.
11026  *
11027  * Originally Released Under LGPL - original licence link has changed is not relivant.
11028  *
11029  * Fork - LGPL
11030  * <script type="text/javascript">
11031  */
11032
11033
11034 /**
11035  * @class Roo.CompositeElement
11036  * Standard composite class. Creates a Roo.Element for every element in the collection.
11037  * <br><br>
11038  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11039  * actions will be performed on all the elements in this collection.</b>
11040  * <br><br>
11041  * All methods return <i>this</i> and can be chained.
11042  <pre><code>
11043  var els = Roo.select("#some-el div.some-class", true);
11044  // or select directly from an existing element
11045  var el = Roo.get('some-el');
11046  el.select('div.some-class', true);
11047
11048  els.setWidth(100); // all elements become 100 width
11049  els.hide(true); // all elements fade out and hide
11050  // or
11051  els.setWidth(100).hide(true);
11052  </code></pre>
11053  */
11054 Roo.CompositeElement = function(els){
11055     this.elements = [];
11056     this.addElements(els);
11057 };
11058 Roo.CompositeElement.prototype = {
11059     isComposite: true,
11060     addElements : function(els){
11061         if(!els) {
11062             return this;
11063         }
11064         if(typeof els == "string"){
11065             els = Roo.Element.selectorFunction(els);
11066         }
11067         var yels = this.elements;
11068         var index = yels.length-1;
11069         for(var i = 0, len = els.length; i < len; i++) {
11070                 yels[++index] = Roo.get(els[i]);
11071         }
11072         return this;
11073     },
11074
11075     /**
11076     * Clears this composite and adds the elements returned by the passed selector.
11077     * @param {String/Array} els A string CSS selector, an array of elements or an element
11078     * @return {CompositeElement} this
11079     */
11080     fill : function(els){
11081         this.elements = [];
11082         this.add(els);
11083         return this;
11084     },
11085
11086     /**
11087     * Filters this composite to only elements that match the passed selector.
11088     * @param {String} selector A string CSS selector
11089     * @param {Boolean} inverse return inverse filter (not matches)
11090     * @return {CompositeElement} this
11091     */
11092     filter : function(selector, inverse){
11093         var els = [];
11094         inverse = inverse || false;
11095         this.each(function(el){
11096             var match = inverse ? !el.is(selector) : el.is(selector);
11097             if(match){
11098                 els[els.length] = el.dom;
11099             }
11100         });
11101         this.fill(els);
11102         return this;
11103     },
11104
11105     invoke : function(fn, args){
11106         var els = this.elements;
11107         for(var i = 0, len = els.length; i < len; i++) {
11108                 Roo.Element.prototype[fn].apply(els[i], args);
11109         }
11110         return this;
11111     },
11112     /**
11113     * Adds elements to this composite.
11114     * @param {String/Array} els A string CSS selector, an array of elements or an element
11115     * @return {CompositeElement} this
11116     */
11117     add : function(els){
11118         if(typeof els == "string"){
11119             this.addElements(Roo.Element.selectorFunction(els));
11120         }else if(els.length !== undefined){
11121             this.addElements(els);
11122         }else{
11123             this.addElements([els]);
11124         }
11125         return this;
11126     },
11127     /**
11128     * Calls the passed function passing (el, this, index) for each element in this composite.
11129     * @param {Function} fn The function to call
11130     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11131     * @return {CompositeElement} this
11132     */
11133     each : function(fn, scope){
11134         var els = this.elements;
11135         for(var i = 0, len = els.length; i < len; i++){
11136             if(fn.call(scope || els[i], els[i], this, i) === false) {
11137                 break;
11138             }
11139         }
11140         return this;
11141     },
11142
11143     /**
11144      * Returns the Element object at the specified index
11145      * @param {Number} index
11146      * @return {Roo.Element}
11147      */
11148     item : function(index){
11149         return this.elements[index] || null;
11150     },
11151
11152     /**
11153      * Returns the first Element
11154      * @return {Roo.Element}
11155      */
11156     first : function(){
11157         return this.item(0);
11158     },
11159
11160     /**
11161      * Returns the last Element
11162      * @return {Roo.Element}
11163      */
11164     last : function(){
11165         return this.item(this.elements.length-1);
11166     },
11167
11168     /**
11169      * Returns the number of elements in this composite
11170      * @return Number
11171      */
11172     getCount : function(){
11173         return this.elements.length;
11174     },
11175
11176     /**
11177      * Returns true if this composite contains the passed element
11178      * @return Boolean
11179      */
11180     contains : function(el){
11181         return this.indexOf(el) !== -1;
11182     },
11183
11184     /**
11185      * Returns true if this composite contains the passed element
11186      * @return Boolean
11187      */
11188     indexOf : function(el){
11189         return this.elements.indexOf(Roo.get(el));
11190     },
11191
11192
11193     /**
11194     * Removes the specified element(s).
11195     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11196     * or an array of any of those.
11197     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11198     * @return {CompositeElement} this
11199     */
11200     removeElement : function(el, removeDom){
11201         if(el instanceof Array){
11202             for(var i = 0, len = el.length; i < len; i++){
11203                 this.removeElement(el[i]);
11204             }
11205             return this;
11206         }
11207         var index = typeof el == 'number' ? el : this.indexOf(el);
11208         if(index !== -1){
11209             if(removeDom){
11210                 var d = this.elements[index];
11211                 if(d.dom){
11212                     d.remove();
11213                 }else{
11214                     d.parentNode.removeChild(d);
11215                 }
11216             }
11217             this.elements.splice(index, 1);
11218         }
11219         return this;
11220     },
11221
11222     /**
11223     * Replaces the specified element with the passed element.
11224     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11225     * to replace.
11226     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11227     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11228     * @return {CompositeElement} this
11229     */
11230     replaceElement : function(el, replacement, domReplace){
11231         var index = typeof el == 'number' ? el : this.indexOf(el);
11232         if(index !== -1){
11233             if(domReplace){
11234                 this.elements[index].replaceWith(replacement);
11235             }else{
11236                 this.elements.splice(index, 1, Roo.get(replacement))
11237             }
11238         }
11239         return this;
11240     },
11241
11242     /**
11243      * Removes all elements.
11244      */
11245     clear : function(){
11246         this.elements = [];
11247     }
11248 };
11249 (function(){
11250     Roo.CompositeElement.createCall = function(proto, fnName){
11251         if(!proto[fnName]){
11252             proto[fnName] = function(){
11253                 return this.invoke(fnName, arguments);
11254             };
11255         }
11256     };
11257     for(var fnName in Roo.Element.prototype){
11258         if(typeof Roo.Element.prototype[fnName] == "function"){
11259             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11260         }
11261     };
11262 })();
11263 /*
11264  * Based on:
11265  * Ext JS Library 1.1.1
11266  * Copyright(c) 2006-2007, Ext JS, LLC.
11267  *
11268  * Originally Released Under LGPL - original licence link has changed is not relivant.
11269  *
11270  * Fork - LGPL
11271  * <script type="text/javascript">
11272  */
11273
11274 /**
11275  * @class Roo.CompositeElementLite
11276  * @extends Roo.CompositeElement
11277  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11278  <pre><code>
11279  var els = Roo.select("#some-el div.some-class");
11280  // or select directly from an existing element
11281  var el = Roo.get('some-el');
11282  el.select('div.some-class');
11283
11284  els.setWidth(100); // all elements become 100 width
11285  els.hide(true); // all elements fade out and hide
11286  // or
11287  els.setWidth(100).hide(true);
11288  </code></pre><br><br>
11289  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11290  * actions will be performed on all the elements in this collection.</b>
11291  */
11292 Roo.CompositeElementLite = function(els){
11293     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11294     this.el = new Roo.Element.Flyweight();
11295 };
11296 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11297     addElements : function(els){
11298         if(els){
11299             if(els instanceof Array){
11300                 this.elements = this.elements.concat(els);
11301             }else{
11302                 var yels = this.elements;
11303                 var index = yels.length-1;
11304                 for(var i = 0, len = els.length; i < len; i++) {
11305                     yels[++index] = els[i];
11306                 }
11307             }
11308         }
11309         return this;
11310     },
11311     invoke : function(fn, args){
11312         var els = this.elements;
11313         var el = this.el;
11314         for(var i = 0, len = els.length; i < len; i++) {
11315             el.dom = els[i];
11316                 Roo.Element.prototype[fn].apply(el, args);
11317         }
11318         return this;
11319     },
11320     /**
11321      * Returns a flyweight Element of the dom element object at the specified index
11322      * @param {Number} index
11323      * @return {Roo.Element}
11324      */
11325     item : function(index){
11326         if(!this.elements[index]){
11327             return null;
11328         }
11329         this.el.dom = this.elements[index];
11330         return this.el;
11331     },
11332
11333     // fixes scope with flyweight
11334     addListener : function(eventName, handler, scope, opt){
11335         var els = this.elements;
11336         for(var i = 0, len = els.length; i < len; i++) {
11337             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11338         }
11339         return this;
11340     },
11341
11342     /**
11343     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11344     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11345     * a reference to the dom node, use el.dom.</b>
11346     * @param {Function} fn The function to call
11347     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11348     * @return {CompositeElement} this
11349     */
11350     each : function(fn, scope){
11351         var els = this.elements;
11352         var el = this.el;
11353         for(var i = 0, len = els.length; i < len; i++){
11354             el.dom = els[i];
11355                 if(fn.call(scope || el, el, this, i) === false){
11356                 break;
11357             }
11358         }
11359         return this;
11360     },
11361
11362     indexOf : function(el){
11363         return this.elements.indexOf(Roo.getDom(el));
11364     },
11365
11366     replaceElement : function(el, replacement, domReplace){
11367         var index = typeof el == 'number' ? el : this.indexOf(el);
11368         if(index !== -1){
11369             replacement = Roo.getDom(replacement);
11370             if(domReplace){
11371                 var d = this.elements[index];
11372                 d.parentNode.insertBefore(replacement, d);
11373                 d.parentNode.removeChild(d);
11374             }
11375             this.elements.splice(index, 1, replacement);
11376         }
11377         return this;
11378     }
11379 });
11380 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11381
11382 /*
11383  * Based on:
11384  * Ext JS Library 1.1.1
11385  * Copyright(c) 2006-2007, Ext JS, LLC.
11386  *
11387  * Originally Released Under LGPL - original licence link has changed is not relivant.
11388  *
11389  * Fork - LGPL
11390  * <script type="text/javascript">
11391  */
11392
11393  
11394
11395 /**
11396  * @class Roo.data.Connection
11397  * @extends Roo.util.Observable
11398  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11399  * either to a configured URL, or to a URL specified at request time.<br><br>
11400  * <p>
11401  * Requests made by this class are asynchronous, and will return immediately. No data from
11402  * the server will be available to the statement immediately following the {@link #request} call.
11403  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11404  * <p>
11405  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11406  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11407  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11408  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11409  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11410  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11411  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11412  * standard DOM methods.
11413  * @constructor
11414  * @param {Object} config a configuration object.
11415  */
11416 Roo.data.Connection = function(config){
11417     Roo.apply(this, config);
11418     this.addEvents({
11419         /**
11420          * @event beforerequest
11421          * Fires before a network request is made to retrieve a data object.
11422          * @param {Connection} conn This Connection object.
11423          * @param {Object} options The options config object passed to the {@link #request} method.
11424          */
11425         "beforerequest" : true,
11426         /**
11427          * @event requestcomplete
11428          * Fires if the request was successfully completed.
11429          * @param {Connection} conn This Connection object.
11430          * @param {Object} response The XHR object containing the response data.
11431          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11432          * @param {Object} options The options config object passed to the {@link #request} method.
11433          */
11434         "requestcomplete" : true,
11435         /**
11436          * @event requestexception
11437          * Fires if an error HTTP status was returned from the server.
11438          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11439          * @param {Connection} conn This Connection object.
11440          * @param {Object} response The XHR object containing the response data.
11441          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11442          * @param {Object} options The options config object passed to the {@link #request} method.
11443          */
11444         "requestexception" : true
11445     });
11446     Roo.data.Connection.superclass.constructor.call(this);
11447 };
11448
11449 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11450     /**
11451      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11452      */
11453     /**
11454      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11455      * extra parameters to each request made by this object. (defaults to undefined)
11456      */
11457     /**
11458      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11459      *  to each request made by this object. (defaults to undefined)
11460      */
11461     /**
11462      * @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)
11463      */
11464     /**
11465      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11466      */
11467     timeout : 30000,
11468     /**
11469      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11470      * @type Boolean
11471      */
11472     autoAbort:false,
11473
11474     /**
11475      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11476      * @type Boolean
11477      */
11478     disableCaching: true,
11479
11480     /**
11481      * Sends an HTTP request to a remote server.
11482      * @param {Object} options An object which may contain the following properties:<ul>
11483      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11484      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11485      * request, a url encoded string or a function to call to get either.</li>
11486      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11487      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11488      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11489      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11490      * <li>options {Object} The parameter to the request call.</li>
11491      * <li>success {Boolean} True if the request succeeded.</li>
11492      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11493      * </ul></li>
11494      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11495      * The callback is passed the following parameters:<ul>
11496      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11497      * <li>options {Object} The parameter to the request call.</li>
11498      * </ul></li>
11499      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11500      * The callback is passed the following parameters:<ul>
11501      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11502      * <li>options {Object} The parameter to the request call.</li>
11503      * </ul></li>
11504      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11505      * for the callback function. Defaults to the browser window.</li>
11506      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11507      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11508      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11509      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11510      * params for the post data. Any params will be appended to the URL.</li>
11511      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11512      * </ul>
11513      * @return {Number} transactionId
11514      */
11515     request : function(o){
11516         if(this.fireEvent("beforerequest", this, o) !== false){
11517             var p = o.params;
11518
11519             if(typeof p == "function"){
11520                 p = p.call(o.scope||window, o);
11521             }
11522             if(typeof p == "object"){
11523                 p = Roo.urlEncode(o.params);
11524             }
11525             if(this.extraParams){
11526                 var extras = Roo.urlEncode(this.extraParams);
11527                 p = p ? (p + '&' + extras) : extras;
11528             }
11529
11530             var url = o.url || this.url;
11531             if(typeof url == 'function'){
11532                 url = url.call(o.scope||window, o);
11533             }
11534
11535             if(o.form){
11536                 var form = Roo.getDom(o.form);
11537                 url = url || form.action;
11538
11539                 var enctype = form.getAttribute("enctype");
11540                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11541                     return this.doFormUpload(o, p, url);
11542                 }
11543                 var f = Roo.lib.Ajax.serializeForm(form);
11544                 p = p ? (p + '&' + f) : f;
11545             }
11546
11547             var hs = o.headers;
11548             if(this.defaultHeaders){
11549                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11550                 if(!o.headers){
11551                     o.headers = hs;
11552                 }
11553             }
11554
11555             var cb = {
11556                 success: this.handleResponse,
11557                 failure: this.handleFailure,
11558                 scope: this,
11559                 argument: {options: o},
11560                 timeout : o.timeout || this.timeout
11561             };
11562
11563             var method = o.method||this.method||(p ? "POST" : "GET");
11564
11565             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11566                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11567             }
11568
11569             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11570                 if(o.autoAbort){
11571                     this.abort();
11572                 }
11573             }else if(this.autoAbort !== false){
11574                 this.abort();
11575             }
11576
11577             if((method == 'GET' && p) || o.xmlData){
11578                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11579                 p = '';
11580             }
11581             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11582             return this.transId;
11583         }else{
11584             Roo.callback(o.callback, o.scope, [o, null, null]);
11585             return null;
11586         }
11587     },
11588
11589     /**
11590      * Determine whether this object has a request outstanding.
11591      * @param {Number} transactionId (Optional) defaults to the last transaction
11592      * @return {Boolean} True if there is an outstanding request.
11593      */
11594     isLoading : function(transId){
11595         if(transId){
11596             return Roo.lib.Ajax.isCallInProgress(transId);
11597         }else{
11598             return this.transId ? true : false;
11599         }
11600     },
11601
11602     /**
11603      * Aborts any outstanding request.
11604      * @param {Number} transactionId (Optional) defaults to the last transaction
11605      */
11606     abort : function(transId){
11607         if(transId || this.isLoading()){
11608             Roo.lib.Ajax.abort(transId || this.transId);
11609         }
11610     },
11611
11612     // private
11613     handleResponse : function(response){
11614         this.transId = false;
11615         var options = response.argument.options;
11616         response.argument = options ? options.argument : null;
11617         this.fireEvent("requestcomplete", this, response, options);
11618         Roo.callback(options.success, options.scope, [response, options]);
11619         Roo.callback(options.callback, options.scope, [options, true, response]);
11620     },
11621
11622     // private
11623     handleFailure : function(response, e){
11624         this.transId = false;
11625         var options = response.argument.options;
11626         response.argument = options ? options.argument : null;
11627         this.fireEvent("requestexception", this, response, options, e);
11628         Roo.callback(options.failure, options.scope, [response, options]);
11629         Roo.callback(options.callback, options.scope, [options, false, response]);
11630     },
11631
11632     // private
11633     doFormUpload : function(o, ps, url){
11634         var id = Roo.id();
11635         var frame = document.createElement('iframe');
11636         frame.id = id;
11637         frame.name = id;
11638         frame.className = 'x-hidden';
11639         if(Roo.isIE){
11640             frame.src = Roo.SSL_SECURE_URL;
11641         }
11642         document.body.appendChild(frame);
11643
11644         if(Roo.isIE){
11645            document.frames[id].name = id;
11646         }
11647
11648         var form = Roo.getDom(o.form);
11649         form.target = id;
11650         form.method = 'POST';
11651         form.enctype = form.encoding = 'multipart/form-data';
11652         if(url){
11653             form.action = url;
11654         }
11655
11656         var hiddens, hd;
11657         if(ps){ // add dynamic params
11658             hiddens = [];
11659             ps = Roo.urlDecode(ps, false);
11660             for(var k in ps){
11661                 if(ps.hasOwnProperty(k)){
11662                     hd = document.createElement('input');
11663                     hd.type = 'hidden';
11664                     hd.name = k;
11665                     hd.value = ps[k];
11666                     form.appendChild(hd);
11667                     hiddens.push(hd);
11668                 }
11669             }
11670         }
11671
11672         function cb(){
11673             var r = {  // bogus response object
11674                 responseText : '',
11675                 responseXML : null
11676             };
11677
11678             r.argument = o ? o.argument : null;
11679
11680             try { //
11681                 var doc;
11682                 if(Roo.isIE){
11683                     doc = frame.contentWindow.document;
11684                 }else {
11685                     doc = (frame.contentDocument || window.frames[id].document);
11686                 }
11687                 if(doc && doc.body){
11688                     r.responseText = doc.body.innerHTML;
11689                 }
11690                 if(doc && doc.XMLDocument){
11691                     r.responseXML = doc.XMLDocument;
11692                 }else {
11693                     r.responseXML = doc;
11694                 }
11695             }
11696             catch(e) {
11697                 // ignore
11698             }
11699
11700             Roo.EventManager.removeListener(frame, 'load', cb, this);
11701
11702             this.fireEvent("requestcomplete", this, r, o);
11703             Roo.callback(o.success, o.scope, [r, o]);
11704             Roo.callback(o.callback, o.scope, [o, true, r]);
11705
11706             setTimeout(function(){document.body.removeChild(frame);}, 100);
11707         }
11708
11709         Roo.EventManager.on(frame, 'load', cb, this);
11710         form.submit();
11711
11712         if(hiddens){ // remove dynamic params
11713             for(var i = 0, len = hiddens.length; i < len; i++){
11714                 form.removeChild(hiddens[i]);
11715             }
11716         }
11717     }
11718 });
11719 /*
11720  * Based on:
11721  * Ext JS Library 1.1.1
11722  * Copyright(c) 2006-2007, Ext JS, LLC.
11723  *
11724  * Originally Released Under LGPL - original licence link has changed is not relivant.
11725  *
11726  * Fork - LGPL
11727  * <script type="text/javascript">
11728  */
11729  
11730 /**
11731  * Global Ajax request class.
11732  * 
11733  * @class Roo.Ajax
11734  * @extends Roo.data.Connection
11735  * @static
11736  * 
11737  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11738  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11739  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11740  * @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)
11741  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11742  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11743  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11744  */
11745 Roo.Ajax = new Roo.data.Connection({
11746     // fix up the docs
11747     /**
11748      * @scope Roo.Ajax
11749      * @type {Boolear} 
11750      */
11751     autoAbort : false,
11752
11753     /**
11754      * Serialize the passed form into a url encoded string
11755      * @scope Roo.Ajax
11756      * @param {String/HTMLElement} form
11757      * @return {String}
11758      */
11759     serializeForm : function(form){
11760         return Roo.lib.Ajax.serializeForm(form);
11761     }
11762 });/*
11763  * Based on:
11764  * Ext JS Library 1.1.1
11765  * Copyright(c) 2006-2007, Ext JS, LLC.
11766  *
11767  * Originally Released Under LGPL - original licence link has changed is not relivant.
11768  *
11769  * Fork - LGPL
11770  * <script type="text/javascript">
11771  */
11772
11773  
11774 /**
11775  * @class Roo.UpdateManager
11776  * @extends Roo.util.Observable
11777  * Provides AJAX-style update for Element object.<br><br>
11778  * Usage:<br>
11779  * <pre><code>
11780  * // Get it from a Roo.Element object
11781  * var el = Roo.get("foo");
11782  * var mgr = el.getUpdateManager();
11783  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11784  * ...
11785  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11786  * <br>
11787  * // or directly (returns the same UpdateManager instance)
11788  * var mgr = new Roo.UpdateManager("myElementId");
11789  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11790  * mgr.on("update", myFcnNeedsToKnow);
11791  * <br>
11792    // short handed call directly from the element object
11793    Roo.get("foo").load({
11794         url: "bar.php",
11795         scripts:true,
11796         params: "for=bar",
11797         text: "Loading Foo..."
11798    });
11799  * </code></pre>
11800  * @constructor
11801  * Create new UpdateManager directly.
11802  * @param {String/HTMLElement/Roo.Element} el The element to update
11803  * @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).
11804  */
11805 Roo.UpdateManager = function(el, forceNew){
11806     el = Roo.get(el);
11807     if(!forceNew && el.updateManager){
11808         return el.updateManager;
11809     }
11810     /**
11811      * The Element object
11812      * @type Roo.Element
11813      */
11814     this.el = el;
11815     /**
11816      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11817      * @type String
11818      */
11819     this.defaultUrl = null;
11820
11821     this.addEvents({
11822         /**
11823          * @event beforeupdate
11824          * Fired before an update is made, return false from your handler and the update is cancelled.
11825          * @param {Roo.Element} el
11826          * @param {String/Object/Function} url
11827          * @param {String/Object} params
11828          */
11829         "beforeupdate": true,
11830         /**
11831          * @event update
11832          * Fired after successful update is made.
11833          * @param {Roo.Element} el
11834          * @param {Object} oResponseObject The response Object
11835          */
11836         "update": true,
11837         /**
11838          * @event failure
11839          * Fired on update failure.
11840          * @param {Roo.Element} el
11841          * @param {Object} oResponseObject The response Object
11842          */
11843         "failure": true
11844     });
11845     var d = Roo.UpdateManager.defaults;
11846     /**
11847      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11848      * @type String
11849      */
11850     this.sslBlankUrl = d.sslBlankUrl;
11851     /**
11852      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11853      * @type Boolean
11854      */
11855     this.disableCaching = d.disableCaching;
11856     /**
11857      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11858      * @type String
11859      */
11860     this.indicatorText = d.indicatorText;
11861     /**
11862      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11863      * @type String
11864      */
11865     this.showLoadIndicator = d.showLoadIndicator;
11866     /**
11867      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11868      * @type Number
11869      */
11870     this.timeout = d.timeout;
11871
11872     /**
11873      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11874      * @type Boolean
11875      */
11876     this.loadScripts = d.loadScripts;
11877
11878     /**
11879      * Transaction object of current executing transaction
11880      */
11881     this.transaction = null;
11882
11883     /**
11884      * @private
11885      */
11886     this.autoRefreshProcId = null;
11887     /**
11888      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11889      * @type Function
11890      */
11891     this.refreshDelegate = this.refresh.createDelegate(this);
11892     /**
11893      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11894      * @type Function
11895      */
11896     this.updateDelegate = this.update.createDelegate(this);
11897     /**
11898      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11899      * @type Function
11900      */
11901     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11902     /**
11903      * @private
11904      */
11905     this.successDelegate = this.processSuccess.createDelegate(this);
11906     /**
11907      * @private
11908      */
11909     this.failureDelegate = this.processFailure.createDelegate(this);
11910
11911     if(!this.renderer){
11912      /**
11913       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11914       */
11915     this.renderer = new Roo.UpdateManager.BasicRenderer();
11916     }
11917     
11918     Roo.UpdateManager.superclass.constructor.call(this);
11919 };
11920
11921 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11922     /**
11923      * Get the Element this UpdateManager is bound to
11924      * @return {Roo.Element} The element
11925      */
11926     getEl : function(){
11927         return this.el;
11928     },
11929     /**
11930      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11931      * @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:
11932 <pre><code>
11933 um.update({<br/>
11934     url: "your-url.php",<br/>
11935     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11936     callback: yourFunction,<br/>
11937     scope: yourObject, //(optional scope)  <br/>
11938     discardUrl: false, <br/>
11939     nocache: false,<br/>
11940     text: "Loading...",<br/>
11941     timeout: 30,<br/>
11942     scripts: false<br/>
11943 });
11944 </code></pre>
11945      * The only required property is url. The optional properties nocache, text and scripts
11946      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11947      * @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}
11948      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11949      * @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.
11950      */
11951     update : function(url, params, callback, discardUrl){
11952         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11953             var method = this.method,
11954                 cfg;
11955             if(typeof url == "object"){ // must be config object
11956                 cfg = url;
11957                 url = cfg.url;
11958                 params = params || cfg.params;
11959                 callback = callback || cfg.callback;
11960                 discardUrl = discardUrl || cfg.discardUrl;
11961                 if(callback && cfg.scope){
11962                     callback = callback.createDelegate(cfg.scope);
11963                 }
11964                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11965                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11966                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11967                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11968                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11969             }
11970             this.showLoading();
11971             if(!discardUrl){
11972                 this.defaultUrl = url;
11973             }
11974             if(typeof url == "function"){
11975                 url = url.call(this);
11976             }
11977
11978             method = method || (params ? "POST" : "GET");
11979             if(method == "GET"){
11980                 url = this.prepareUrl(url);
11981             }
11982
11983             var o = Roo.apply(cfg ||{}, {
11984                 url : url,
11985                 params: params,
11986                 success: this.successDelegate,
11987                 failure: this.failureDelegate,
11988                 callback: undefined,
11989                 timeout: (this.timeout*1000),
11990                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11991             });
11992             Roo.log("updated manager called with timeout of " + o.timeout);
11993             this.transaction = Roo.Ajax.request(o);
11994         }
11995     },
11996
11997     /**
11998      * 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.
11999      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12000      * @param {String/HTMLElement} form The form Id or form element
12001      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12002      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12003      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12004      */
12005     formUpdate : function(form, url, reset, callback){
12006         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12007             if(typeof url == "function"){
12008                 url = url.call(this);
12009             }
12010             form = Roo.getDom(form);
12011             this.transaction = Roo.Ajax.request({
12012                 form: form,
12013                 url:url,
12014                 success: this.successDelegate,
12015                 failure: this.failureDelegate,
12016                 timeout: (this.timeout*1000),
12017                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12018             });
12019             this.showLoading.defer(1, this);
12020         }
12021     },
12022
12023     /**
12024      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12025      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12026      */
12027     refresh : function(callback){
12028         if(this.defaultUrl == null){
12029             return;
12030         }
12031         this.update(this.defaultUrl, null, callback, true);
12032     },
12033
12034     /**
12035      * Set this element to auto refresh.
12036      * @param {Number} interval How often to update (in seconds).
12037      * @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)
12038      * @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}
12039      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12040      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12041      */
12042     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12043         if(refreshNow){
12044             this.update(url || this.defaultUrl, params, callback, true);
12045         }
12046         if(this.autoRefreshProcId){
12047             clearInterval(this.autoRefreshProcId);
12048         }
12049         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12050     },
12051
12052     /**
12053      * Stop auto refresh on this element.
12054      */
12055      stopAutoRefresh : function(){
12056         if(this.autoRefreshProcId){
12057             clearInterval(this.autoRefreshProcId);
12058             delete this.autoRefreshProcId;
12059         }
12060     },
12061
12062     isAutoRefreshing : function(){
12063        return this.autoRefreshProcId ? true : false;
12064     },
12065     /**
12066      * Called to update the element to "Loading" state. Override to perform custom action.
12067      */
12068     showLoading : function(){
12069         if(this.showLoadIndicator){
12070             this.el.update(this.indicatorText);
12071         }
12072     },
12073
12074     /**
12075      * Adds unique parameter to query string if disableCaching = true
12076      * @private
12077      */
12078     prepareUrl : function(url){
12079         if(this.disableCaching){
12080             var append = "_dc=" + (new Date().getTime());
12081             if(url.indexOf("?") !== -1){
12082                 url += "&" + append;
12083             }else{
12084                 url += "?" + append;
12085             }
12086         }
12087         return url;
12088     },
12089
12090     /**
12091      * @private
12092      */
12093     processSuccess : function(response){
12094         this.transaction = null;
12095         if(response.argument.form && response.argument.reset){
12096             try{ // put in try/catch since some older FF releases had problems with this
12097                 response.argument.form.reset();
12098             }catch(e){}
12099         }
12100         if(this.loadScripts){
12101             this.renderer.render(this.el, response, this,
12102                 this.updateComplete.createDelegate(this, [response]));
12103         }else{
12104             this.renderer.render(this.el, response, this);
12105             this.updateComplete(response);
12106         }
12107     },
12108
12109     updateComplete : function(response){
12110         this.fireEvent("update", this.el, response);
12111         if(typeof response.argument.callback == "function"){
12112             response.argument.callback(this.el, true, response);
12113         }
12114     },
12115
12116     /**
12117      * @private
12118      */
12119     processFailure : function(response){
12120         this.transaction = null;
12121         this.fireEvent("failure", this.el, response);
12122         if(typeof response.argument.callback == "function"){
12123             response.argument.callback(this.el, false, response);
12124         }
12125     },
12126
12127     /**
12128      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12129      * @param {Object} renderer The object implementing the render() method
12130      */
12131     setRenderer : function(renderer){
12132         this.renderer = renderer;
12133     },
12134
12135     getRenderer : function(){
12136        return this.renderer;
12137     },
12138
12139     /**
12140      * Set the defaultUrl used for updates
12141      * @param {String/Function} defaultUrl The url or a function to call to get the url
12142      */
12143     setDefaultUrl : function(defaultUrl){
12144         this.defaultUrl = defaultUrl;
12145     },
12146
12147     /**
12148      * Aborts the executing transaction
12149      */
12150     abort : function(){
12151         if(this.transaction){
12152             Roo.Ajax.abort(this.transaction);
12153         }
12154     },
12155
12156     /**
12157      * Returns true if an update is in progress
12158      * @return {Boolean}
12159      */
12160     isUpdating : function(){
12161         if(this.transaction){
12162             return Roo.Ajax.isLoading(this.transaction);
12163         }
12164         return false;
12165     }
12166 });
12167
12168 /**
12169  * @class Roo.UpdateManager.defaults
12170  * @static (not really - but it helps the doc tool)
12171  * The defaults collection enables customizing the default properties of UpdateManager
12172  */
12173    Roo.UpdateManager.defaults = {
12174        /**
12175          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12176          * @type Number
12177          */
12178          timeout : 30,
12179
12180          /**
12181          * True to process scripts by default (Defaults to false).
12182          * @type Boolean
12183          */
12184         loadScripts : false,
12185
12186         /**
12187         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12188         * @type String
12189         */
12190         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12191         /**
12192          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12193          * @type Boolean
12194          */
12195         disableCaching : false,
12196         /**
12197          * Whether to show indicatorText when loading (Defaults to true).
12198          * @type Boolean
12199          */
12200         showLoadIndicator : true,
12201         /**
12202          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12203          * @type String
12204          */
12205         indicatorText : '<div class="loading-indicator">Loading...</div>'
12206    };
12207
12208 /**
12209  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12210  *Usage:
12211  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12212  * @param {String/HTMLElement/Roo.Element} el The element to update
12213  * @param {String} url The url
12214  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12215  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12216  * @static
12217  * @deprecated
12218  * @member Roo.UpdateManager
12219  */
12220 Roo.UpdateManager.updateElement = function(el, url, params, options){
12221     var um = Roo.get(el, true).getUpdateManager();
12222     Roo.apply(um, options);
12223     um.update(url, params, options ? options.callback : null);
12224 };
12225 // alias for backwards compat
12226 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12227 /**
12228  * @class Roo.UpdateManager.BasicRenderer
12229  * Default Content renderer. Updates the elements innerHTML with the responseText.
12230  */
12231 Roo.UpdateManager.BasicRenderer = function(){};
12232
12233 Roo.UpdateManager.BasicRenderer.prototype = {
12234     /**
12235      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12236      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12237      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12238      * @param {Roo.Element} el The element being rendered
12239      * @param {Object} response The YUI Connect response object
12240      * @param {UpdateManager} updateManager The calling update manager
12241      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12242      */
12243      render : function(el, response, updateManager, callback){
12244         el.update(response.responseText, updateManager.loadScripts, callback);
12245     }
12246 };
12247 /*
12248  * Based on:
12249  * Roo JS
12250  * (c)) Alan Knowles
12251  * Licence : LGPL
12252  */
12253
12254
12255 /**
12256  * @class Roo.DomTemplate
12257  * @extends Roo.Template
12258  * An effort at a dom based template engine..
12259  *
12260  * Similar to XTemplate, except it uses dom parsing to create the template..
12261  *
12262  * Supported features:
12263  *
12264  *  Tags:
12265
12266 <pre><code>
12267       {a_variable} - output encoded.
12268       {a_variable.format:("Y-m-d")} - call a method on the variable
12269       {a_variable:raw} - unencoded output
12270       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12271       {a_variable:this.method_on_template(...)} - call a method on the template object.
12272  
12273 </code></pre>
12274  *  The tpl tag:
12275 <pre><code>
12276         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12277         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12278         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12279         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12280   
12281 </code></pre>
12282  *      
12283  */
12284 Roo.DomTemplate = function()
12285 {
12286      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12287      if (this.html) {
12288         this.compile();
12289      }
12290 };
12291
12292
12293 Roo.extend(Roo.DomTemplate, Roo.Template, {
12294     /**
12295      * id counter for sub templates.
12296      */
12297     id : 0,
12298     /**
12299      * flag to indicate if dom parser is inside a pre,
12300      * it will strip whitespace if not.
12301      */
12302     inPre : false,
12303     
12304     /**
12305      * The various sub templates
12306      */
12307     tpls : false,
12308     
12309     
12310     
12311     /**
12312      *
12313      * basic tag replacing syntax
12314      * WORD:WORD()
12315      *
12316      * // you can fake an object call by doing this
12317      *  x.t:(test,tesT) 
12318      * 
12319      */
12320     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12321     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12322     
12323     iterChild : function (node, method) {
12324         
12325         var oldPre = this.inPre;
12326         if (node.tagName == 'PRE') {
12327             this.inPre = true;
12328         }
12329         for( var i = 0; i < node.childNodes.length; i++) {
12330             method.call(this, node.childNodes[i]);
12331         }
12332         this.inPre = oldPre;
12333     },
12334     
12335     
12336     
12337     /**
12338      * compile the template
12339      *
12340      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12341      *
12342      */
12343     compile: function()
12344     {
12345         var s = this.html;
12346         
12347         // covert the html into DOM...
12348         var doc = false;
12349         var div =false;
12350         try {
12351             doc = document.implementation.createHTMLDocument("");
12352             doc.documentElement.innerHTML =   this.html  ;
12353             div = doc.documentElement;
12354         } catch (e) {
12355             // old IE... - nasty -- it causes all sorts of issues.. with
12356             // images getting pulled from server..
12357             div = document.createElement('div');
12358             div.innerHTML = this.html;
12359         }
12360         //doc.documentElement.innerHTML = htmlBody
12361          
12362         
12363         
12364         this.tpls = [];
12365         var _t = this;
12366         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12367         
12368         var tpls = this.tpls;
12369         
12370         // create a top level template from the snippet..
12371         
12372         //Roo.log(div.innerHTML);
12373         
12374         var tpl = {
12375             uid : 'master',
12376             id : this.id++,
12377             attr : false,
12378             value : false,
12379             body : div.innerHTML,
12380             
12381             forCall : false,
12382             execCall : false,
12383             dom : div,
12384             isTop : true
12385             
12386         };
12387         tpls.unshift(tpl);
12388         
12389         
12390         // compile them...
12391         this.tpls = [];
12392         Roo.each(tpls, function(tp){
12393             this.compileTpl(tp);
12394             this.tpls[tp.id] = tp;
12395         }, this);
12396         
12397         this.master = tpls[0];
12398         return this;
12399         
12400         
12401     },
12402     
12403     compileNode : function(node, istop) {
12404         // test for
12405         //Roo.log(node);
12406         
12407         
12408         // skip anything not a tag..
12409         if (node.nodeType != 1) {
12410             if (node.nodeType == 3 && !this.inPre) {
12411                 // reduce white space..
12412                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12413                 
12414             }
12415             return;
12416         }
12417         
12418         var tpl = {
12419             uid : false,
12420             id : false,
12421             attr : false,
12422             value : false,
12423             body : '',
12424             
12425             forCall : false,
12426             execCall : false,
12427             dom : false,
12428             isTop : istop
12429             
12430             
12431         };
12432         
12433         
12434         switch(true) {
12435             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12436             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12437             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12438             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12439             // no default..
12440         }
12441         
12442         
12443         if (!tpl.attr) {
12444             // just itterate children..
12445             this.iterChild(node,this.compileNode);
12446             return;
12447         }
12448         tpl.uid = this.id++;
12449         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12450         node.removeAttribute('roo-'+ tpl.attr);
12451         if (tpl.attr != 'name') {
12452             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12453             node.parentNode.replaceChild(placeholder,  node);
12454         } else {
12455             
12456             var placeholder =  document.createElement('span');
12457             placeholder.className = 'roo-tpl-' + tpl.value;
12458             node.parentNode.replaceChild(placeholder,  node);
12459         }
12460         
12461         // parent now sees '{domtplXXXX}
12462         this.iterChild(node,this.compileNode);
12463         
12464         // we should now have node body...
12465         var div = document.createElement('div');
12466         div.appendChild(node);
12467         tpl.dom = node;
12468         // this has the unfortunate side effect of converting tagged attributes
12469         // eg. href="{...}" into %7C...%7D
12470         // this has been fixed by searching for those combo's although it's a bit hacky..
12471         
12472         
12473         tpl.body = div.innerHTML;
12474         
12475         
12476          
12477         tpl.id = tpl.uid;
12478         switch(tpl.attr) {
12479             case 'for' :
12480                 switch (tpl.value) {
12481                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12482                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12483                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12484                 }
12485                 break;
12486             
12487             case 'exec':
12488                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12489                 break;
12490             
12491             case 'if':     
12492                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12493                 break;
12494             
12495             case 'name':
12496                 tpl.id  = tpl.value; // replace non characters???
12497                 break;
12498             
12499         }
12500         
12501         
12502         this.tpls.push(tpl);
12503         
12504         
12505         
12506     },
12507     
12508     
12509     
12510     
12511     /**
12512      * Compile a segment of the template into a 'sub-template'
12513      *
12514      * 
12515      * 
12516      *
12517      */
12518     compileTpl : function(tpl)
12519     {
12520         var fm = Roo.util.Format;
12521         var useF = this.disableFormats !== true;
12522         
12523         var sep = Roo.isGecko ? "+\n" : ",\n";
12524         
12525         var undef = function(str) {
12526             Roo.debug && Roo.log("Property not found :"  + str);
12527             return '';
12528         };
12529           
12530         //Roo.log(tpl.body);
12531         
12532         
12533         
12534         var fn = function(m, lbrace, name, format, args)
12535         {
12536             //Roo.log("ARGS");
12537             //Roo.log(arguments);
12538             args = args ? args.replace(/\\'/g,"'") : args;
12539             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12540             if (typeof(format) == 'undefined') {
12541                 format =  'htmlEncode'; 
12542             }
12543             if (format == 'raw' ) {
12544                 format = false;
12545             }
12546             
12547             if(name.substr(0, 6) == 'domtpl'){
12548                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12549             }
12550             
12551             // build an array of options to determine if value is undefined..
12552             
12553             // basically get 'xxxx.yyyy' then do
12554             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12555             //    (function () { Roo.log("Property not found"); return ''; })() :
12556             //    ......
12557             
12558             var udef_ar = [];
12559             var lookfor = '';
12560             Roo.each(name.split('.'), function(st) {
12561                 lookfor += (lookfor.length ? '.': '') + st;
12562                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12563             });
12564             
12565             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12566             
12567             
12568             if(format && useF){
12569                 
12570                 args = args ? ',' + args : "";
12571                  
12572                 if(format.substr(0, 5) != "this."){
12573                     format = "fm." + format + '(';
12574                 }else{
12575                     format = 'this.call("'+ format.substr(5) + '", ';
12576                     args = ", values";
12577                 }
12578                 
12579                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12580             }
12581              
12582             if (args && args.length) {
12583                 // called with xxyx.yuu:(test,test)
12584                 // change to ()
12585                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12586             }
12587             // raw.. - :raw modifier..
12588             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12589             
12590         };
12591         var body;
12592         // branched to use + in gecko and [].join() in others
12593         if(Roo.isGecko){
12594             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12595                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12596                     "';};};";
12597         }else{
12598             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12599             body.push(tpl.body.replace(/(\r\n|\n)/g,
12600                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12601             body.push("'].join('');};};");
12602             body = body.join('');
12603         }
12604         
12605         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12606        
12607         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12608         eval(body);
12609         
12610         return this;
12611     },
12612      
12613     /**
12614      * same as applyTemplate, except it's done to one of the subTemplates
12615      * when using named templates, you can do:
12616      *
12617      * var str = pl.applySubTemplate('your-name', values);
12618      *
12619      * 
12620      * @param {Number} id of the template
12621      * @param {Object} values to apply to template
12622      * @param {Object} parent (normaly the instance of this object)
12623      */
12624     applySubTemplate : function(id, values, parent)
12625     {
12626         
12627         
12628         var t = this.tpls[id];
12629         
12630         
12631         try { 
12632             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12633                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12634                 return '';
12635             }
12636         } catch(e) {
12637             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12638             Roo.log(values);
12639           
12640             return '';
12641         }
12642         try { 
12643             
12644             if(t.execCall && t.execCall.call(this, values, parent)){
12645                 return '';
12646             }
12647         } catch(e) {
12648             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12649             Roo.log(values);
12650             return '';
12651         }
12652         
12653         try {
12654             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12655             parent = t.target ? values : parent;
12656             if(t.forCall && vs instanceof Array){
12657                 var buf = [];
12658                 for(var i = 0, len = vs.length; i < len; i++){
12659                     try {
12660                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12661                     } catch (e) {
12662                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12663                         Roo.log(e.body);
12664                         //Roo.log(t.compiled);
12665                         Roo.log(vs[i]);
12666                     }   
12667                 }
12668                 return buf.join('');
12669             }
12670         } catch (e) {
12671             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12672             Roo.log(values);
12673             return '';
12674         }
12675         try {
12676             return t.compiled.call(this, vs, parent);
12677         } catch (e) {
12678             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12679             Roo.log(e.body);
12680             //Roo.log(t.compiled);
12681             Roo.log(values);
12682             return '';
12683         }
12684     },
12685
12686    
12687
12688     applyTemplate : function(values){
12689         return this.master.compiled.call(this, values, {});
12690         //var s = this.subs;
12691     },
12692
12693     apply : function(){
12694         return this.applyTemplate.apply(this, arguments);
12695     }
12696
12697  });
12698
12699 Roo.DomTemplate.from = function(el){
12700     el = Roo.getDom(el);
12701     return new Roo.Domtemplate(el.value || el.innerHTML);
12702 };/*
12703  * Based on:
12704  * Ext JS Library 1.1.1
12705  * Copyright(c) 2006-2007, Ext JS, LLC.
12706  *
12707  * Originally Released Under LGPL - original licence link has changed is not relivant.
12708  *
12709  * Fork - LGPL
12710  * <script type="text/javascript">
12711  */
12712
12713 /**
12714  * @class Roo.util.DelayedTask
12715  * Provides a convenient method of performing setTimeout where a new
12716  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12717  * You can use this class to buffer
12718  * the keypress events for a certain number of milliseconds, and perform only if they stop
12719  * for that amount of time.
12720  * @constructor The parameters to this constructor serve as defaults and are not required.
12721  * @param {Function} fn (optional) The default function to timeout
12722  * @param {Object} scope (optional) The default scope of that timeout
12723  * @param {Array} args (optional) The default Array of arguments
12724  */
12725 Roo.util.DelayedTask = function(fn, scope, args){
12726     var id = null, d, t;
12727
12728     var call = function(){
12729         var now = new Date().getTime();
12730         if(now - t >= d){
12731             clearInterval(id);
12732             id = null;
12733             fn.apply(scope, args || []);
12734         }
12735     };
12736     /**
12737      * Cancels any pending timeout and queues a new one
12738      * @param {Number} delay The milliseconds to delay
12739      * @param {Function} newFn (optional) Overrides function passed to constructor
12740      * @param {Object} newScope (optional) Overrides scope passed to constructor
12741      * @param {Array} newArgs (optional) Overrides args passed to constructor
12742      */
12743     this.delay = function(delay, newFn, newScope, newArgs){
12744         if(id && delay != d){
12745             this.cancel();
12746         }
12747         d = delay;
12748         t = new Date().getTime();
12749         fn = newFn || fn;
12750         scope = newScope || scope;
12751         args = newArgs || args;
12752         if(!id){
12753             id = setInterval(call, d);
12754         }
12755     };
12756
12757     /**
12758      * Cancel the last queued timeout
12759      */
12760     this.cancel = function(){
12761         if(id){
12762             clearInterval(id);
12763             id = null;
12764         }
12765     };
12766 };/*
12767  * Based on:
12768  * Ext JS Library 1.1.1
12769  * Copyright(c) 2006-2007, Ext JS, LLC.
12770  *
12771  * Originally Released Under LGPL - original licence link has changed is not relivant.
12772  *
12773  * Fork - LGPL
12774  * <script type="text/javascript">
12775  */
12776  
12777  
12778 Roo.util.TaskRunner = function(interval){
12779     interval = interval || 10;
12780     var tasks = [], removeQueue = [];
12781     var id = 0;
12782     var running = false;
12783
12784     var stopThread = function(){
12785         running = false;
12786         clearInterval(id);
12787         id = 0;
12788     };
12789
12790     var startThread = function(){
12791         if(!running){
12792             running = true;
12793             id = setInterval(runTasks, interval);
12794         }
12795     };
12796
12797     var removeTask = function(task){
12798         removeQueue.push(task);
12799         if(task.onStop){
12800             task.onStop();
12801         }
12802     };
12803
12804     var runTasks = function(){
12805         if(removeQueue.length > 0){
12806             for(var i = 0, len = removeQueue.length; i < len; i++){
12807                 tasks.remove(removeQueue[i]);
12808             }
12809             removeQueue = [];
12810             if(tasks.length < 1){
12811                 stopThread();
12812                 return;
12813             }
12814         }
12815         var now = new Date().getTime();
12816         for(var i = 0, len = tasks.length; i < len; ++i){
12817             var t = tasks[i];
12818             var itime = now - t.taskRunTime;
12819             if(t.interval <= itime){
12820                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12821                 t.taskRunTime = now;
12822                 if(rt === false || t.taskRunCount === t.repeat){
12823                     removeTask(t);
12824                     return;
12825                 }
12826             }
12827             if(t.duration && t.duration <= (now - t.taskStartTime)){
12828                 removeTask(t);
12829             }
12830         }
12831     };
12832
12833     /**
12834      * Queues a new task.
12835      * @param {Object} task
12836      */
12837     this.start = function(task){
12838         tasks.push(task);
12839         task.taskStartTime = new Date().getTime();
12840         task.taskRunTime = 0;
12841         task.taskRunCount = 0;
12842         startThread();
12843         return task;
12844     };
12845
12846     this.stop = function(task){
12847         removeTask(task);
12848         return task;
12849     };
12850
12851     this.stopAll = function(){
12852         stopThread();
12853         for(var i = 0, len = tasks.length; i < len; i++){
12854             if(tasks[i].onStop){
12855                 tasks[i].onStop();
12856             }
12857         }
12858         tasks = [];
12859         removeQueue = [];
12860     };
12861 };
12862
12863 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12864  * Based on:
12865  * Ext JS Library 1.1.1
12866  * Copyright(c) 2006-2007, Ext JS, LLC.
12867  *
12868  * Originally Released Under LGPL - original licence link has changed is not relivant.
12869  *
12870  * Fork - LGPL
12871  * <script type="text/javascript">
12872  */
12873
12874  
12875 /**
12876  * @class Roo.util.MixedCollection
12877  * @extends Roo.util.Observable
12878  * A Collection class that maintains both numeric indexes and keys and exposes events.
12879  * @constructor
12880  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12881  * collection (defaults to false)
12882  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12883  * and return the key value for that item.  This is used when available to look up the key on items that
12884  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12885  * equivalent to providing an implementation for the {@link #getKey} method.
12886  */
12887 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12888     this.items = [];
12889     this.map = {};
12890     this.keys = [];
12891     this.length = 0;
12892     this.addEvents({
12893         /**
12894          * @event clear
12895          * Fires when the collection is cleared.
12896          */
12897         "clear" : true,
12898         /**
12899          * @event add
12900          * Fires when an item is added to the collection.
12901          * @param {Number} index The index at which the item was added.
12902          * @param {Object} o The item added.
12903          * @param {String} key The key associated with the added item.
12904          */
12905         "add" : true,
12906         /**
12907          * @event replace
12908          * Fires when an item is replaced in the collection.
12909          * @param {String} key he key associated with the new added.
12910          * @param {Object} old The item being replaced.
12911          * @param {Object} new The new item.
12912          */
12913         "replace" : true,
12914         /**
12915          * @event remove
12916          * Fires when an item is removed from the collection.
12917          * @param {Object} o The item being removed.
12918          * @param {String} key (optional) The key associated with the removed item.
12919          */
12920         "remove" : true,
12921         "sort" : true
12922     });
12923     this.allowFunctions = allowFunctions === true;
12924     if(keyFn){
12925         this.getKey = keyFn;
12926     }
12927     Roo.util.MixedCollection.superclass.constructor.call(this);
12928 };
12929
12930 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12931     allowFunctions : false,
12932     
12933 /**
12934  * Adds an item to the collection.
12935  * @param {String} key The key to associate with the item
12936  * @param {Object} o The item to add.
12937  * @return {Object} The item added.
12938  */
12939     add : function(key, o){
12940         if(arguments.length == 1){
12941             o = arguments[0];
12942             key = this.getKey(o);
12943         }
12944         if(typeof key == "undefined" || key === null){
12945             this.length++;
12946             this.items.push(o);
12947             this.keys.push(null);
12948         }else{
12949             var old = this.map[key];
12950             if(old){
12951                 return this.replace(key, o);
12952             }
12953             this.length++;
12954             this.items.push(o);
12955             this.map[key] = o;
12956             this.keys.push(key);
12957         }
12958         this.fireEvent("add", this.length-1, o, key);
12959         return o;
12960     },
12961        
12962 /**
12963   * MixedCollection has a generic way to fetch keys if you implement getKey.
12964 <pre><code>
12965 // normal way
12966 var mc = new Roo.util.MixedCollection();
12967 mc.add(someEl.dom.id, someEl);
12968 mc.add(otherEl.dom.id, otherEl);
12969 //and so on
12970
12971 // using getKey
12972 var mc = new Roo.util.MixedCollection();
12973 mc.getKey = function(el){
12974    return el.dom.id;
12975 };
12976 mc.add(someEl);
12977 mc.add(otherEl);
12978
12979 // or via the constructor
12980 var mc = new Roo.util.MixedCollection(false, function(el){
12981    return el.dom.id;
12982 });
12983 mc.add(someEl);
12984 mc.add(otherEl);
12985 </code></pre>
12986  * @param o {Object} The item for which to find the key.
12987  * @return {Object} The key for the passed item.
12988  */
12989     getKey : function(o){
12990          return o.id; 
12991     },
12992    
12993 /**
12994  * Replaces an item in the collection.
12995  * @param {String} key The key associated with the item to replace, or the item to replace.
12996  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12997  * @return {Object}  The new item.
12998  */
12999     replace : function(key, o){
13000         if(arguments.length == 1){
13001             o = arguments[0];
13002             key = this.getKey(o);
13003         }
13004         var old = this.item(key);
13005         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13006              return this.add(key, o);
13007         }
13008         var index = this.indexOfKey(key);
13009         this.items[index] = o;
13010         this.map[key] = o;
13011         this.fireEvent("replace", key, old, o);
13012         return o;
13013     },
13014    
13015 /**
13016  * Adds all elements of an Array or an Object to the collection.
13017  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13018  * an Array of values, each of which are added to the collection.
13019  */
13020     addAll : function(objs){
13021         if(arguments.length > 1 || objs instanceof Array){
13022             var args = arguments.length > 1 ? arguments : objs;
13023             for(var i = 0, len = args.length; i < len; i++){
13024                 this.add(args[i]);
13025             }
13026         }else{
13027             for(var key in objs){
13028                 if(this.allowFunctions || typeof objs[key] != "function"){
13029                     this.add(key, objs[key]);
13030                 }
13031             }
13032         }
13033     },
13034    
13035 /**
13036  * Executes the specified function once for every item in the collection, passing each
13037  * item as the first and only parameter. returning false from the function will stop the iteration.
13038  * @param {Function} fn The function to execute for each item.
13039  * @param {Object} scope (optional) The scope in which to execute the function.
13040  */
13041     each : function(fn, scope){
13042         var items = [].concat(this.items); // each safe for removal
13043         for(var i = 0, len = items.length; i < len; i++){
13044             if(fn.call(scope || items[i], items[i], i, len) === false){
13045                 break;
13046             }
13047         }
13048     },
13049    
13050 /**
13051  * Executes the specified function once for every key in the collection, passing each
13052  * key, and its associated item as the first two parameters.
13053  * @param {Function} fn The function to execute for each item.
13054  * @param {Object} scope (optional) The scope in which to execute the function.
13055  */
13056     eachKey : function(fn, scope){
13057         for(var i = 0, len = this.keys.length; i < len; i++){
13058             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13059         }
13060     },
13061    
13062 /**
13063  * Returns the first item in the collection which elicits a true return value from the
13064  * passed selection function.
13065  * @param {Function} fn The selection function to execute for each item.
13066  * @param {Object} scope (optional) The scope in which to execute the function.
13067  * @return {Object} The first item in the collection which returned true from the selection function.
13068  */
13069     find : function(fn, scope){
13070         for(var i = 0, len = this.items.length; i < len; i++){
13071             if(fn.call(scope || window, this.items[i], this.keys[i])){
13072                 return this.items[i];
13073             }
13074         }
13075         return null;
13076     },
13077    
13078 /**
13079  * Inserts an item at the specified index in the collection.
13080  * @param {Number} index The index to insert the item at.
13081  * @param {String} key The key to associate with the new item, or the item itself.
13082  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13083  * @return {Object} The item inserted.
13084  */
13085     insert : function(index, key, o){
13086         if(arguments.length == 2){
13087             o = arguments[1];
13088             key = this.getKey(o);
13089         }
13090         if(index >= this.length){
13091             return this.add(key, o);
13092         }
13093         this.length++;
13094         this.items.splice(index, 0, o);
13095         if(typeof key != "undefined" && key != null){
13096             this.map[key] = o;
13097         }
13098         this.keys.splice(index, 0, key);
13099         this.fireEvent("add", index, o, key);
13100         return o;
13101     },
13102    
13103 /**
13104  * Removed an item from the collection.
13105  * @param {Object} o The item to remove.
13106  * @return {Object} The item removed.
13107  */
13108     remove : function(o){
13109         return this.removeAt(this.indexOf(o));
13110     },
13111    
13112 /**
13113  * Remove an item from a specified index in the collection.
13114  * @param {Number} index The index within the collection of the item to remove.
13115  */
13116     removeAt : function(index){
13117         if(index < this.length && index >= 0){
13118             this.length--;
13119             var o = this.items[index];
13120             this.items.splice(index, 1);
13121             var key = this.keys[index];
13122             if(typeof key != "undefined"){
13123                 delete this.map[key];
13124             }
13125             this.keys.splice(index, 1);
13126             this.fireEvent("remove", o, key);
13127         }
13128     },
13129    
13130 /**
13131  * Removed an item associated with the passed key fom the collection.
13132  * @param {String} key The key of the item to remove.
13133  */
13134     removeKey : function(key){
13135         return this.removeAt(this.indexOfKey(key));
13136     },
13137    
13138 /**
13139  * Returns the number of items in the collection.
13140  * @return {Number} the number of items in the collection.
13141  */
13142     getCount : function(){
13143         return this.length; 
13144     },
13145    
13146 /**
13147  * Returns index within the collection of the passed Object.
13148  * @param {Object} o The item to find the index of.
13149  * @return {Number} index of the item.
13150  */
13151     indexOf : function(o){
13152         if(!this.items.indexOf){
13153             for(var i = 0, len = this.items.length; i < len; i++){
13154                 if(this.items[i] == o) {
13155                     return i;
13156                 }
13157             }
13158             return -1;
13159         }else{
13160             return this.items.indexOf(o);
13161         }
13162     },
13163    
13164 /**
13165  * Returns index within the collection of the passed key.
13166  * @param {String} key The key to find the index of.
13167  * @return {Number} index of the key.
13168  */
13169     indexOfKey : function(key){
13170         if(!this.keys.indexOf){
13171             for(var i = 0, len = this.keys.length; i < len; i++){
13172                 if(this.keys[i] == key) {
13173                     return i;
13174                 }
13175             }
13176             return -1;
13177         }else{
13178             return this.keys.indexOf(key);
13179         }
13180     },
13181    
13182 /**
13183  * Returns the item associated with the passed key OR index. Key has priority over index.
13184  * @param {String/Number} key The key or index of the item.
13185  * @return {Object} The item associated with the passed key.
13186  */
13187     item : function(key){
13188         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13189         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13190     },
13191     
13192 /**
13193  * Returns the item at the specified index.
13194  * @param {Number} index The index of the item.
13195  * @return {Object}
13196  */
13197     itemAt : function(index){
13198         return this.items[index];
13199     },
13200     
13201 /**
13202  * Returns the item associated with the passed key.
13203  * @param {String/Number} key The key of the item.
13204  * @return {Object} The item associated with the passed key.
13205  */
13206     key : function(key){
13207         return this.map[key];
13208     },
13209    
13210 /**
13211  * Returns true if the collection contains the passed Object as an item.
13212  * @param {Object} o  The Object to look for in the collection.
13213  * @return {Boolean} True if the collection contains the Object as an item.
13214  */
13215     contains : function(o){
13216         return this.indexOf(o) != -1;
13217     },
13218    
13219 /**
13220  * Returns true if the collection contains the passed Object as a key.
13221  * @param {String} key The key to look for in the collection.
13222  * @return {Boolean} True if the collection contains the Object as a key.
13223  */
13224     containsKey : function(key){
13225         return typeof this.map[key] != "undefined";
13226     },
13227    
13228 /**
13229  * Removes all items from the collection.
13230  */
13231     clear : function(){
13232         this.length = 0;
13233         this.items = [];
13234         this.keys = [];
13235         this.map = {};
13236         this.fireEvent("clear");
13237     },
13238    
13239 /**
13240  * Returns the first item in the collection.
13241  * @return {Object} the first item in the collection..
13242  */
13243     first : function(){
13244         return this.items[0]; 
13245     },
13246    
13247 /**
13248  * Returns the last item in the collection.
13249  * @return {Object} the last item in the collection..
13250  */
13251     last : function(){
13252         return this.items[this.length-1];   
13253     },
13254     
13255     _sort : function(property, dir, fn){
13256         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13257         fn = fn || function(a, b){
13258             return a-b;
13259         };
13260         var c = [], k = this.keys, items = this.items;
13261         for(var i = 0, len = items.length; i < len; i++){
13262             c[c.length] = {key: k[i], value: items[i], index: i};
13263         }
13264         c.sort(function(a, b){
13265             var v = fn(a[property], b[property]) * dsc;
13266             if(v == 0){
13267                 v = (a.index < b.index ? -1 : 1);
13268             }
13269             return v;
13270         });
13271         for(var i = 0, len = c.length; i < len; i++){
13272             items[i] = c[i].value;
13273             k[i] = c[i].key;
13274         }
13275         this.fireEvent("sort", this);
13276     },
13277     
13278     /**
13279      * Sorts this collection with the passed comparison function
13280      * @param {String} direction (optional) "ASC" or "DESC"
13281      * @param {Function} fn (optional) comparison function
13282      */
13283     sort : function(dir, fn){
13284         this._sort("value", dir, fn);
13285     },
13286     
13287     /**
13288      * Sorts this collection by keys
13289      * @param {String} direction (optional) "ASC" or "DESC"
13290      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13291      */
13292     keySort : function(dir, fn){
13293         this._sort("key", dir, fn || function(a, b){
13294             return String(a).toUpperCase()-String(b).toUpperCase();
13295         });
13296     },
13297     
13298     /**
13299      * Returns a range of items in this collection
13300      * @param {Number} startIndex (optional) defaults to 0
13301      * @param {Number} endIndex (optional) default to the last item
13302      * @return {Array} An array of items
13303      */
13304     getRange : function(start, end){
13305         var items = this.items;
13306         if(items.length < 1){
13307             return [];
13308         }
13309         start = start || 0;
13310         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13311         var r = [];
13312         if(start <= end){
13313             for(var i = start; i <= end; i++) {
13314                     r[r.length] = items[i];
13315             }
13316         }else{
13317             for(var i = start; i >= end; i--) {
13318                     r[r.length] = items[i];
13319             }
13320         }
13321         return r;
13322     },
13323         
13324     /**
13325      * Filter the <i>objects</i> in this collection by a specific property. 
13326      * Returns a new collection that has been filtered.
13327      * @param {String} property A property on your objects
13328      * @param {String/RegExp} value Either string that the property values 
13329      * should start with or a RegExp to test against the property
13330      * @return {MixedCollection} The new filtered collection
13331      */
13332     filter : function(property, value){
13333         if(!value.exec){ // not a regex
13334             value = String(value);
13335             if(value.length == 0){
13336                 return this.clone();
13337             }
13338             value = new RegExp("^" + Roo.escapeRe(value), "i");
13339         }
13340         return this.filterBy(function(o){
13341             return o && value.test(o[property]);
13342         });
13343         },
13344     
13345     /**
13346      * Filter by a function. * Returns a new collection that has been filtered.
13347      * The passed function will be called with each 
13348      * object in the collection. If the function returns true, the value is included 
13349      * otherwise it is filtered.
13350      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13351      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13352      * @return {MixedCollection} The new filtered collection
13353      */
13354     filterBy : function(fn, scope){
13355         var r = new Roo.util.MixedCollection();
13356         r.getKey = this.getKey;
13357         var k = this.keys, it = this.items;
13358         for(var i = 0, len = it.length; i < len; i++){
13359             if(fn.call(scope||this, it[i], k[i])){
13360                                 r.add(k[i], it[i]);
13361                         }
13362         }
13363         return r;
13364     },
13365     
13366     /**
13367      * Creates a duplicate of this collection
13368      * @return {MixedCollection}
13369      */
13370     clone : function(){
13371         var r = new Roo.util.MixedCollection();
13372         var k = this.keys, it = this.items;
13373         for(var i = 0, len = it.length; i < len; i++){
13374             r.add(k[i], it[i]);
13375         }
13376         r.getKey = this.getKey;
13377         return r;
13378     }
13379 });
13380 /**
13381  * Returns the item associated with the passed key or index.
13382  * @method
13383  * @param {String/Number} key The key or index of the item.
13384  * @return {Object} The item associated with the passed key.
13385  */
13386 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13387  * Based on:
13388  * Ext JS Library 1.1.1
13389  * Copyright(c) 2006-2007, Ext JS, LLC.
13390  *
13391  * Originally Released Under LGPL - original licence link has changed is not relivant.
13392  *
13393  * Fork - LGPL
13394  * <script type="text/javascript">
13395  */
13396 /**
13397  * @class Roo.util.JSON
13398  * Modified version of Douglas Crockford"s json.js that doesn"t
13399  * mess with the Object prototype 
13400  * http://www.json.org/js.html
13401  * @singleton
13402  */
13403 Roo.util.JSON = new (function(){
13404     var useHasOwn = {}.hasOwnProperty ? true : false;
13405     
13406     // crashes Safari in some instances
13407     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13408     
13409     var pad = function(n) {
13410         return n < 10 ? "0" + n : n;
13411     };
13412     
13413     var m = {
13414         "\b": '\\b',
13415         "\t": '\\t',
13416         "\n": '\\n',
13417         "\f": '\\f',
13418         "\r": '\\r',
13419         '"' : '\\"',
13420         "\\": '\\\\'
13421     };
13422
13423     var encodeString = function(s){
13424         if (/["\\\x00-\x1f]/.test(s)) {
13425             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13426                 var c = m[b];
13427                 if(c){
13428                     return c;
13429                 }
13430                 c = b.charCodeAt();
13431                 return "\\u00" +
13432                     Math.floor(c / 16).toString(16) +
13433                     (c % 16).toString(16);
13434             }) + '"';
13435         }
13436         return '"' + s + '"';
13437     };
13438     
13439     var encodeArray = function(o){
13440         var a = ["["], b, i, l = o.length, v;
13441             for (i = 0; i < l; i += 1) {
13442                 v = o[i];
13443                 switch (typeof v) {
13444                     case "undefined":
13445                     case "function":
13446                     case "unknown":
13447                         break;
13448                     default:
13449                         if (b) {
13450                             a.push(',');
13451                         }
13452                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13453                         b = true;
13454                 }
13455             }
13456             a.push("]");
13457             return a.join("");
13458     };
13459     
13460     var encodeDate = function(o){
13461         return '"' + o.getFullYear() + "-" +
13462                 pad(o.getMonth() + 1) + "-" +
13463                 pad(o.getDate()) + "T" +
13464                 pad(o.getHours()) + ":" +
13465                 pad(o.getMinutes()) + ":" +
13466                 pad(o.getSeconds()) + '"';
13467     };
13468     
13469     /**
13470      * Encodes an Object, Array or other value
13471      * @param {Mixed} o The variable to encode
13472      * @return {String} The JSON string
13473      */
13474     this.encode = function(o)
13475     {
13476         // should this be extended to fully wrap stringify..
13477         
13478         if(typeof o == "undefined" || o === null){
13479             return "null";
13480         }else if(o instanceof Array){
13481             return encodeArray(o);
13482         }else if(o instanceof Date){
13483             return encodeDate(o);
13484         }else if(typeof o == "string"){
13485             return encodeString(o);
13486         }else if(typeof o == "number"){
13487             return isFinite(o) ? String(o) : "null";
13488         }else if(typeof o == "boolean"){
13489             return String(o);
13490         }else {
13491             var a = ["{"], b, i, v;
13492             for (i in o) {
13493                 if(!useHasOwn || o.hasOwnProperty(i)) {
13494                     v = o[i];
13495                     switch (typeof v) {
13496                     case "undefined":
13497                     case "function":
13498                     case "unknown":
13499                         break;
13500                     default:
13501                         if(b){
13502                             a.push(',');
13503                         }
13504                         a.push(this.encode(i), ":",
13505                                 v === null ? "null" : this.encode(v));
13506                         b = true;
13507                     }
13508                 }
13509             }
13510             a.push("}");
13511             return a.join("");
13512         }
13513     };
13514     
13515     /**
13516      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13517      * @param {String} json The JSON string
13518      * @return {Object} The resulting object
13519      */
13520     this.decode = function(json){
13521         
13522         return  /** eval:var:json */ eval("(" + json + ')');
13523     };
13524 })();
13525 /** 
13526  * Shorthand for {@link Roo.util.JSON#encode}
13527  * @member Roo encode 
13528  * @method */
13529 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13530 /** 
13531  * Shorthand for {@link Roo.util.JSON#decode}
13532  * @member Roo decode 
13533  * @method */
13534 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13535 /*
13536  * Based on:
13537  * Ext JS Library 1.1.1
13538  * Copyright(c) 2006-2007, Ext JS, LLC.
13539  *
13540  * Originally Released Under LGPL - original licence link has changed is not relivant.
13541  *
13542  * Fork - LGPL
13543  * <script type="text/javascript">
13544  */
13545  
13546 /**
13547  * @class Roo.util.Format
13548  * Reusable data formatting functions
13549  * @singleton
13550  */
13551 Roo.util.Format = function(){
13552     var trimRe = /^\s+|\s+$/g;
13553     return {
13554         /**
13555          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13556          * @param {String} value The string to truncate
13557          * @param {Number} length The maximum length to allow before truncating
13558          * @return {String} The converted text
13559          */
13560         ellipsis : function(value, len){
13561             if(value && value.length > len){
13562                 return value.substr(0, len-3)+"...";
13563             }
13564             return value;
13565         },
13566
13567         /**
13568          * Checks a reference and converts it to empty string if it is undefined
13569          * @param {Mixed} value Reference to check
13570          * @return {Mixed} Empty string if converted, otherwise the original value
13571          */
13572         undef : function(value){
13573             return typeof value != "undefined" ? value : "";
13574         },
13575
13576         /**
13577          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13578          * @param {String} value The string to encode
13579          * @return {String} The encoded text
13580          */
13581         htmlEncode : function(value){
13582             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13583         },
13584
13585         /**
13586          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13587          * @param {String} value The string to decode
13588          * @return {String} The decoded text
13589          */
13590         htmlDecode : function(value){
13591             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13592         },
13593
13594         /**
13595          * Trims any whitespace from either side of a string
13596          * @param {String} value The text to trim
13597          * @return {String} The trimmed text
13598          */
13599         trim : function(value){
13600             return String(value).replace(trimRe, "");
13601         },
13602
13603         /**
13604          * Returns a substring from within an original string
13605          * @param {String} value The original text
13606          * @param {Number} start The start index of the substring
13607          * @param {Number} length The length of the substring
13608          * @return {String} The substring
13609          */
13610         substr : function(value, start, length){
13611             return String(value).substr(start, length);
13612         },
13613
13614         /**
13615          * Converts a string to all lower case letters
13616          * @param {String} value The text to convert
13617          * @return {String} The converted text
13618          */
13619         lowercase : function(value){
13620             return String(value).toLowerCase();
13621         },
13622
13623         /**
13624          * Converts a string to all upper case letters
13625          * @param {String} value The text to convert
13626          * @return {String} The converted text
13627          */
13628         uppercase : function(value){
13629             return String(value).toUpperCase();
13630         },
13631
13632         /**
13633          * Converts the first character only of a string to upper case
13634          * @param {String} value The text to convert
13635          * @return {String} The converted text
13636          */
13637         capitalize : function(value){
13638             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13639         },
13640
13641         // private
13642         call : function(value, fn){
13643             if(arguments.length > 2){
13644                 var args = Array.prototype.slice.call(arguments, 2);
13645                 args.unshift(value);
13646                  
13647                 return /** eval:var:value */  eval(fn).apply(window, args);
13648             }else{
13649                 /** eval:var:value */
13650                 return /** eval:var:value */ eval(fn).call(window, value);
13651             }
13652         },
13653
13654        
13655         /**
13656          * safer version of Math.toFixed..??/
13657          * @param {Number/String} value The numeric value to format
13658          * @param {Number/String} value Decimal places 
13659          * @return {String} The formatted currency string
13660          */
13661         toFixed : function(v, n)
13662         {
13663             // why not use to fixed - precision is buggered???
13664             if (!n) {
13665                 return Math.round(v-0);
13666             }
13667             var fact = Math.pow(10,n+1);
13668             v = (Math.round((v-0)*fact))/fact;
13669             var z = (''+fact).substring(2);
13670             if (v == Math.floor(v)) {
13671                 return Math.floor(v) + '.' + z;
13672             }
13673             
13674             // now just padd decimals..
13675             var ps = String(v).split('.');
13676             var fd = (ps[1] + z);
13677             var r = fd.substring(0,n); 
13678             var rm = fd.substring(n); 
13679             if (rm < 5) {
13680                 return ps[0] + '.' + r;
13681             }
13682             r*=1; // turn it into a number;
13683             r++;
13684             if (String(r).length != n) {
13685                 ps[0]*=1;
13686                 ps[0]++;
13687                 r = String(r).substring(1); // chop the end off.
13688             }
13689             
13690             return ps[0] + '.' + r;
13691              
13692         },
13693         
13694         /**
13695          * Format a number as US currency
13696          * @param {Number/String} value The numeric value to format
13697          * @return {String} The formatted currency string
13698          */
13699         usMoney : function(v){
13700             return '$' + Roo.util.Format.number(v);
13701         },
13702         
13703         /**
13704          * Format a number
13705          * eventually this should probably emulate php's number_format
13706          * @param {Number/String} value The numeric value to format
13707          * @param {Number} decimals number of decimal places
13708          * @return {String} The formatted currency string
13709          */
13710         number : function(v,decimals)
13711         {
13712             // multiply and round.
13713             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13714             var mul = Math.pow(10, decimals);
13715             var zero = String(mul).substring(1);
13716             v = (Math.round((v-0)*mul))/mul;
13717             
13718             // if it's '0' number.. then
13719             
13720             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13721             v = String(v);
13722             var ps = v.split('.');
13723             var whole = ps[0];
13724             
13725             
13726             var r = /(\d+)(\d{3})/;
13727             // add comma's
13728             while (r.test(whole)) {
13729                 whole = whole.replace(r, '$1' + ',' + '$2');
13730             }
13731             
13732             
13733             var sub = ps[1] ?
13734                     // has decimals..
13735                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13736                     // does not have decimals
13737                     (decimals ? ('.' + zero) : '');
13738             
13739             
13740             return whole + sub ;
13741         },
13742         
13743         /**
13744          * Parse a value into a formatted date using the specified format pattern.
13745          * @param {Mixed} value The value to format
13746          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13747          * @return {String} The formatted date string
13748          */
13749         date : function(v, format){
13750             if(!v){
13751                 return "";
13752             }
13753             if(!(v instanceof Date)){
13754                 v = new Date(Date.parse(v));
13755             }
13756             return v.dateFormat(format || Roo.util.Format.defaults.date);
13757         },
13758
13759         /**
13760          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13761          * @param {String} format Any valid date format string
13762          * @return {Function} The date formatting function
13763          */
13764         dateRenderer : function(format){
13765             return function(v){
13766                 return Roo.util.Format.date(v, format);  
13767             };
13768         },
13769
13770         // private
13771         stripTagsRE : /<\/?[^>]+>/gi,
13772         
13773         /**
13774          * Strips all HTML tags
13775          * @param {Mixed} value The text from which to strip tags
13776          * @return {String} The stripped text
13777          */
13778         stripTags : function(v){
13779             return !v ? v : String(v).replace(this.stripTagsRE, "");
13780         }
13781     };
13782 }();
13783 Roo.util.Format.defaults = {
13784     date : 'd/M/Y'
13785 };/*
13786  * Based on:
13787  * Ext JS Library 1.1.1
13788  * Copyright(c) 2006-2007, Ext JS, LLC.
13789  *
13790  * Originally Released Under LGPL - original licence link has changed is not relivant.
13791  *
13792  * Fork - LGPL
13793  * <script type="text/javascript">
13794  */
13795
13796
13797  
13798
13799 /**
13800  * @class Roo.MasterTemplate
13801  * @extends Roo.Template
13802  * Provides a template that can have child templates. The syntax is:
13803 <pre><code>
13804 var t = new Roo.MasterTemplate(
13805         '&lt;select name="{name}"&gt;',
13806                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13807         '&lt;/select&gt;'
13808 );
13809 t.add('options', {value: 'foo', text: 'bar'});
13810 // or you can add multiple child elements in one shot
13811 t.addAll('options', [
13812     {value: 'foo', text: 'bar'},
13813     {value: 'foo2', text: 'bar2'},
13814     {value: 'foo3', text: 'bar3'}
13815 ]);
13816 // then append, applying the master template values
13817 t.append('my-form', {name: 'my-select'});
13818 </code></pre>
13819 * A name attribute for the child template is not required if you have only one child
13820 * template or you want to refer to them by index.
13821  */
13822 Roo.MasterTemplate = function(){
13823     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13824     this.originalHtml = this.html;
13825     var st = {};
13826     var m, re = this.subTemplateRe;
13827     re.lastIndex = 0;
13828     var subIndex = 0;
13829     while(m = re.exec(this.html)){
13830         var name = m[1], content = m[2];
13831         st[subIndex] = {
13832             name: name,
13833             index: subIndex,
13834             buffer: [],
13835             tpl : new Roo.Template(content)
13836         };
13837         if(name){
13838             st[name] = st[subIndex];
13839         }
13840         st[subIndex].tpl.compile();
13841         st[subIndex].tpl.call = this.call.createDelegate(this);
13842         subIndex++;
13843     }
13844     this.subCount = subIndex;
13845     this.subs = st;
13846 };
13847 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13848     /**
13849     * The regular expression used to match sub templates
13850     * @type RegExp
13851     * @property
13852     */
13853     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13854
13855     /**
13856      * Applies the passed values to a child template.
13857      * @param {String/Number} name (optional) The name or index of the child template
13858      * @param {Array/Object} values The values to be applied to the template
13859      * @return {MasterTemplate} this
13860      */
13861      add : function(name, values){
13862         if(arguments.length == 1){
13863             values = arguments[0];
13864             name = 0;
13865         }
13866         var s = this.subs[name];
13867         s.buffer[s.buffer.length] = s.tpl.apply(values);
13868         return this;
13869     },
13870
13871     /**
13872      * Applies all the passed values to a child template.
13873      * @param {String/Number} name (optional) The name or index of the child template
13874      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13875      * @param {Boolean} reset (optional) True to reset the template first
13876      * @return {MasterTemplate} this
13877      */
13878     fill : function(name, values, reset){
13879         var a = arguments;
13880         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13881             values = a[0];
13882             name = 0;
13883             reset = a[1];
13884         }
13885         if(reset){
13886             this.reset();
13887         }
13888         for(var i = 0, len = values.length; i < len; i++){
13889             this.add(name, values[i]);
13890         }
13891         return this;
13892     },
13893
13894     /**
13895      * Resets the template for reuse
13896      * @return {MasterTemplate} this
13897      */
13898      reset : function(){
13899         var s = this.subs;
13900         for(var i = 0; i < this.subCount; i++){
13901             s[i].buffer = [];
13902         }
13903         return this;
13904     },
13905
13906     applyTemplate : function(values){
13907         var s = this.subs;
13908         var replaceIndex = -1;
13909         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13910             return s[++replaceIndex].buffer.join("");
13911         });
13912         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13913     },
13914
13915     apply : function(){
13916         return this.applyTemplate.apply(this, arguments);
13917     },
13918
13919     compile : function(){return this;}
13920 });
13921
13922 /**
13923  * Alias for fill().
13924  * @method
13925  */
13926 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13927  /**
13928  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13929  * var tpl = Roo.MasterTemplate.from('element-id');
13930  * @param {String/HTMLElement} el
13931  * @param {Object} config
13932  * @static
13933  */
13934 Roo.MasterTemplate.from = function(el, config){
13935     el = Roo.getDom(el);
13936     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13937 };/*
13938  * Based on:
13939  * Ext JS Library 1.1.1
13940  * Copyright(c) 2006-2007, Ext JS, LLC.
13941  *
13942  * Originally Released Under LGPL - original licence link has changed is not relivant.
13943  *
13944  * Fork - LGPL
13945  * <script type="text/javascript">
13946  */
13947
13948  
13949 /**
13950  * @class Roo.util.CSS
13951  * Utility class for manipulating CSS rules
13952  * @singleton
13953  */
13954 Roo.util.CSS = function(){
13955         var rules = null;
13956         var doc = document;
13957
13958     var camelRe = /(-[a-z])/gi;
13959     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13960
13961    return {
13962    /**
13963     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13964     * tag and appended to the HEAD of the document.
13965     * @param {String|Object} cssText The text containing the css rules
13966     * @param {String} id An id to add to the stylesheet for later removal
13967     * @return {StyleSheet}
13968     */
13969     createStyleSheet : function(cssText, id){
13970         var ss;
13971         var head = doc.getElementsByTagName("head")[0];
13972         var nrules = doc.createElement("style");
13973         nrules.setAttribute("type", "text/css");
13974         if(id){
13975             nrules.setAttribute("id", id);
13976         }
13977         if (typeof(cssText) != 'string') {
13978             // support object maps..
13979             // not sure if this a good idea.. 
13980             // perhaps it should be merged with the general css handling
13981             // and handle js style props.
13982             var cssTextNew = [];
13983             for(var n in cssText) {
13984                 var citems = [];
13985                 for(var k in cssText[n]) {
13986                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13987                 }
13988                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13989                 
13990             }
13991             cssText = cssTextNew.join("\n");
13992             
13993         }
13994        
13995        
13996        if(Roo.isIE){
13997            head.appendChild(nrules);
13998            ss = nrules.styleSheet;
13999            ss.cssText = cssText;
14000        }else{
14001            try{
14002                 nrules.appendChild(doc.createTextNode(cssText));
14003            }catch(e){
14004                nrules.cssText = cssText; 
14005            }
14006            head.appendChild(nrules);
14007            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14008        }
14009        this.cacheStyleSheet(ss);
14010        return ss;
14011    },
14012
14013    /**
14014     * Removes a style or link tag by id
14015     * @param {String} id The id of the tag
14016     */
14017    removeStyleSheet : function(id){
14018        var existing = doc.getElementById(id);
14019        if(existing){
14020            existing.parentNode.removeChild(existing);
14021        }
14022    },
14023
14024    /**
14025     * Dynamically swaps an existing stylesheet reference for a new one
14026     * @param {String} id The id of an existing link tag to remove
14027     * @param {String} url The href of the new stylesheet to include
14028     */
14029    swapStyleSheet : function(id, url){
14030        this.removeStyleSheet(id);
14031        var ss = doc.createElement("link");
14032        ss.setAttribute("rel", "stylesheet");
14033        ss.setAttribute("type", "text/css");
14034        ss.setAttribute("id", id);
14035        ss.setAttribute("href", url);
14036        doc.getElementsByTagName("head")[0].appendChild(ss);
14037    },
14038    
14039    /**
14040     * Refresh the rule cache if you have dynamically added stylesheets
14041     * @return {Object} An object (hash) of rules indexed by selector
14042     */
14043    refreshCache : function(){
14044        return this.getRules(true);
14045    },
14046
14047    // private
14048    cacheStyleSheet : function(stylesheet){
14049        if(!rules){
14050            rules = {};
14051        }
14052        try{// try catch for cross domain access issue
14053            var ssRules = stylesheet.cssRules || stylesheet.rules;
14054            for(var j = ssRules.length-1; j >= 0; --j){
14055                rules[ssRules[j].selectorText] = ssRules[j];
14056            }
14057        }catch(e){}
14058    },
14059    
14060    /**
14061     * Gets all css rules for the document
14062     * @param {Boolean} refreshCache true to refresh the internal cache
14063     * @return {Object} An object (hash) of rules indexed by selector
14064     */
14065    getRules : function(refreshCache){
14066                 if(rules == null || refreshCache){
14067                         rules = {};
14068                         var ds = doc.styleSheets;
14069                         for(var i =0, len = ds.length; i < len; i++){
14070                             try{
14071                         this.cacheStyleSheet(ds[i]);
14072                     }catch(e){} 
14073                 }
14074                 }
14075                 return rules;
14076         },
14077         
14078         /**
14079     * Gets an an individual CSS rule by selector(s)
14080     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14081     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14082     * @return {CSSRule} The CSS rule or null if one is not found
14083     */
14084    getRule : function(selector, refreshCache){
14085                 var rs = this.getRules(refreshCache);
14086                 if(!(selector instanceof Array)){
14087                     return rs[selector];
14088                 }
14089                 for(var i = 0; i < selector.length; i++){
14090                         if(rs[selector[i]]){
14091                                 return rs[selector[i]];
14092                         }
14093                 }
14094                 return null;
14095         },
14096         
14097         
14098         /**
14099     * Updates a rule property
14100     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14101     * @param {String} property The css property
14102     * @param {String} value The new value for the property
14103     * @return {Boolean} true If a rule was found and updated
14104     */
14105    updateRule : function(selector, property, value){
14106                 if(!(selector instanceof Array)){
14107                         var rule = this.getRule(selector);
14108                         if(rule){
14109                                 rule.style[property.replace(camelRe, camelFn)] = value;
14110                                 return true;
14111                         }
14112                 }else{
14113                         for(var i = 0; i < selector.length; i++){
14114                                 if(this.updateRule(selector[i], property, value)){
14115                                         return true;
14116                                 }
14117                         }
14118                 }
14119                 return false;
14120         }
14121    };   
14122 }();/*
14123  * Based on:
14124  * Ext JS Library 1.1.1
14125  * Copyright(c) 2006-2007, Ext JS, LLC.
14126  *
14127  * Originally Released Under LGPL - original licence link has changed is not relivant.
14128  *
14129  * Fork - LGPL
14130  * <script type="text/javascript">
14131  */
14132
14133  
14134
14135 /**
14136  * @class Roo.util.ClickRepeater
14137  * @extends Roo.util.Observable
14138  * 
14139  * A wrapper class which can be applied to any element. Fires a "click" event while the
14140  * mouse is pressed. The interval between firings may be specified in the config but
14141  * defaults to 10 milliseconds.
14142  * 
14143  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14144  * 
14145  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14146  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14147  * Similar to an autorepeat key delay.
14148  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14149  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14150  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14151  *           "interval" and "delay" are ignored. "immediate" is honored.
14152  * @cfg {Boolean} preventDefault True to prevent the default click event
14153  * @cfg {Boolean} stopDefault True to stop the default click event
14154  * 
14155  * @history
14156  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14157  *     2007-02-02 jvs Renamed to ClickRepeater
14158  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14159  *
14160  *  @constructor
14161  * @param {String/HTMLElement/Element} el The element to listen on
14162  * @param {Object} config
14163  **/
14164 Roo.util.ClickRepeater = function(el, config)
14165 {
14166     this.el = Roo.get(el);
14167     this.el.unselectable();
14168
14169     Roo.apply(this, config);
14170
14171     this.addEvents({
14172     /**
14173      * @event mousedown
14174      * Fires when the mouse button is depressed.
14175      * @param {Roo.util.ClickRepeater} this
14176      */
14177         "mousedown" : true,
14178     /**
14179      * @event click
14180      * Fires on a specified interval during the time the element is pressed.
14181      * @param {Roo.util.ClickRepeater} this
14182      */
14183         "click" : true,
14184     /**
14185      * @event mouseup
14186      * Fires when the mouse key is released.
14187      * @param {Roo.util.ClickRepeater} this
14188      */
14189         "mouseup" : true
14190     });
14191
14192     this.el.on("mousedown", this.handleMouseDown, this);
14193     if(this.preventDefault || this.stopDefault){
14194         this.el.on("click", function(e){
14195             if(this.preventDefault){
14196                 e.preventDefault();
14197             }
14198             if(this.stopDefault){
14199                 e.stopEvent();
14200             }
14201         }, this);
14202     }
14203
14204     // allow inline handler
14205     if(this.handler){
14206         this.on("click", this.handler,  this.scope || this);
14207     }
14208
14209     Roo.util.ClickRepeater.superclass.constructor.call(this);
14210 };
14211
14212 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14213     interval : 20,
14214     delay: 250,
14215     preventDefault : true,
14216     stopDefault : false,
14217     timer : 0,
14218
14219     // private
14220     handleMouseDown : function(){
14221         clearTimeout(this.timer);
14222         this.el.blur();
14223         if(this.pressClass){
14224             this.el.addClass(this.pressClass);
14225         }
14226         this.mousedownTime = new Date();
14227
14228         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14229         this.el.on("mouseout", this.handleMouseOut, this);
14230
14231         this.fireEvent("mousedown", this);
14232         this.fireEvent("click", this);
14233         
14234         this.timer = this.click.defer(this.delay || this.interval, this);
14235     },
14236
14237     // private
14238     click : function(){
14239         this.fireEvent("click", this);
14240         this.timer = this.click.defer(this.getInterval(), this);
14241     },
14242
14243     // private
14244     getInterval: function(){
14245         if(!this.accelerate){
14246             return this.interval;
14247         }
14248         var pressTime = this.mousedownTime.getElapsed();
14249         if(pressTime < 500){
14250             return 400;
14251         }else if(pressTime < 1700){
14252             return 320;
14253         }else if(pressTime < 2600){
14254             return 250;
14255         }else if(pressTime < 3500){
14256             return 180;
14257         }else if(pressTime < 4400){
14258             return 140;
14259         }else if(pressTime < 5300){
14260             return 80;
14261         }else if(pressTime < 6200){
14262             return 50;
14263         }else{
14264             return 10;
14265         }
14266     },
14267
14268     // private
14269     handleMouseOut : function(){
14270         clearTimeout(this.timer);
14271         if(this.pressClass){
14272             this.el.removeClass(this.pressClass);
14273         }
14274         this.el.on("mouseover", this.handleMouseReturn, this);
14275     },
14276
14277     // private
14278     handleMouseReturn : function(){
14279         this.el.un("mouseover", this.handleMouseReturn);
14280         if(this.pressClass){
14281             this.el.addClass(this.pressClass);
14282         }
14283         this.click();
14284     },
14285
14286     // private
14287     handleMouseUp : function(){
14288         clearTimeout(this.timer);
14289         this.el.un("mouseover", this.handleMouseReturn);
14290         this.el.un("mouseout", this.handleMouseOut);
14291         Roo.get(document).un("mouseup", this.handleMouseUp);
14292         this.el.removeClass(this.pressClass);
14293         this.fireEvent("mouseup", this);
14294     }
14295 });/*
14296  * Based on:
14297  * Ext JS Library 1.1.1
14298  * Copyright(c) 2006-2007, Ext JS, LLC.
14299  *
14300  * Originally Released Under LGPL - original licence link has changed is not relivant.
14301  *
14302  * Fork - LGPL
14303  * <script type="text/javascript">
14304  */
14305
14306  
14307 /**
14308  * @class Roo.KeyNav
14309  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14310  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14311  * way to implement custom navigation schemes for any UI component.</p>
14312  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14313  * pageUp, pageDown, del, home, end.  Usage:</p>
14314  <pre><code>
14315 var nav = new Roo.KeyNav("my-element", {
14316     "left" : function(e){
14317         this.moveLeft(e.ctrlKey);
14318     },
14319     "right" : function(e){
14320         this.moveRight(e.ctrlKey);
14321     },
14322     "enter" : function(e){
14323         this.save();
14324     },
14325     scope : this
14326 });
14327 </code></pre>
14328  * @constructor
14329  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14330  * @param {Object} config The config
14331  */
14332 Roo.KeyNav = function(el, config){
14333     this.el = Roo.get(el);
14334     Roo.apply(this, config);
14335     if(!this.disabled){
14336         this.disabled = true;
14337         this.enable();
14338     }
14339 };
14340
14341 Roo.KeyNav.prototype = {
14342     /**
14343      * @cfg {Boolean} disabled
14344      * True to disable this KeyNav instance (defaults to false)
14345      */
14346     disabled : false,
14347     /**
14348      * @cfg {String} defaultEventAction
14349      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14350      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14351      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14352      */
14353     defaultEventAction: "stopEvent",
14354     /**
14355      * @cfg {Boolean} forceKeyDown
14356      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14357      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14358      * handle keydown instead of keypress.
14359      */
14360     forceKeyDown : false,
14361
14362     // private
14363     prepareEvent : function(e){
14364         var k = e.getKey();
14365         var h = this.keyToHandler[k];
14366         //if(h && this[h]){
14367         //    e.stopPropagation();
14368         //}
14369         if(Roo.isSafari && h && k >= 37 && k <= 40){
14370             e.stopEvent();
14371         }
14372     },
14373
14374     // private
14375     relay : function(e){
14376         var k = e.getKey();
14377         var h = this.keyToHandler[k];
14378         if(h && this[h]){
14379             if(this.doRelay(e, this[h], h) !== true){
14380                 e[this.defaultEventAction]();
14381             }
14382         }
14383     },
14384
14385     // private
14386     doRelay : function(e, h, hname){
14387         return h.call(this.scope || this, e);
14388     },
14389
14390     // possible handlers
14391     enter : false,
14392     left : false,
14393     right : false,
14394     up : false,
14395     down : false,
14396     tab : false,
14397     esc : false,
14398     pageUp : false,
14399     pageDown : false,
14400     del : false,
14401     home : false,
14402     end : false,
14403
14404     // quick lookup hash
14405     keyToHandler : {
14406         37 : "left",
14407         39 : "right",
14408         38 : "up",
14409         40 : "down",
14410         33 : "pageUp",
14411         34 : "pageDown",
14412         46 : "del",
14413         36 : "home",
14414         35 : "end",
14415         13 : "enter",
14416         27 : "esc",
14417         9  : "tab"
14418     },
14419
14420         /**
14421          * Enable this KeyNav
14422          */
14423         enable: function(){
14424                 if(this.disabled){
14425             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14426             // the EventObject will normalize Safari automatically
14427             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14428                 this.el.on("keydown", this.relay,  this);
14429             }else{
14430                 this.el.on("keydown", this.prepareEvent,  this);
14431                 this.el.on("keypress", this.relay,  this);
14432             }
14433                     this.disabled = false;
14434                 }
14435         },
14436
14437         /**
14438          * Disable this KeyNav
14439          */
14440         disable: function(){
14441                 if(!this.disabled){
14442                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14443                 this.el.un("keydown", this.relay);
14444             }else{
14445                 this.el.un("keydown", this.prepareEvent);
14446                 this.el.un("keypress", this.relay);
14447             }
14448                     this.disabled = true;
14449                 }
14450         }
14451 };/*
14452  * Based on:
14453  * Ext JS Library 1.1.1
14454  * Copyright(c) 2006-2007, Ext JS, LLC.
14455  *
14456  * Originally Released Under LGPL - original licence link has changed is not relivant.
14457  *
14458  * Fork - LGPL
14459  * <script type="text/javascript">
14460  */
14461
14462  
14463 /**
14464  * @class Roo.KeyMap
14465  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14466  * The constructor accepts the same config object as defined by {@link #addBinding}.
14467  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14468  * combination it will call the function with this signature (if the match is a multi-key
14469  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14470  * A KeyMap can also handle a string representation of keys.<br />
14471  * Usage:
14472  <pre><code>
14473 // map one key by key code
14474 var map = new Roo.KeyMap("my-element", {
14475     key: 13, // or Roo.EventObject.ENTER
14476     fn: myHandler,
14477     scope: myObject
14478 });
14479
14480 // map multiple keys to one action by string
14481 var map = new Roo.KeyMap("my-element", {
14482     key: "a\r\n\t",
14483     fn: myHandler,
14484     scope: myObject
14485 });
14486
14487 // map multiple keys to multiple actions by strings and array of codes
14488 var map = new Roo.KeyMap("my-element", [
14489     {
14490         key: [10,13],
14491         fn: function(){ alert("Return was pressed"); }
14492     }, {
14493         key: "abc",
14494         fn: function(){ alert('a, b or c was pressed'); }
14495     }, {
14496         key: "\t",
14497         ctrl:true,
14498         shift:true,
14499         fn: function(){ alert('Control + shift + tab was pressed.'); }
14500     }
14501 ]);
14502 </code></pre>
14503  * <b>Note: A KeyMap starts enabled</b>
14504  * @constructor
14505  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14506  * @param {Object} config The config (see {@link #addBinding})
14507  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14508  */
14509 Roo.KeyMap = function(el, config, eventName){
14510     this.el  = Roo.get(el);
14511     this.eventName = eventName || "keydown";
14512     this.bindings = [];
14513     if(config){
14514         this.addBinding(config);
14515     }
14516     this.enable();
14517 };
14518
14519 Roo.KeyMap.prototype = {
14520     /**
14521      * True to stop the event from bubbling and prevent the default browser action if the
14522      * key was handled by the KeyMap (defaults to false)
14523      * @type Boolean
14524      */
14525     stopEvent : false,
14526
14527     /**
14528      * Add a new binding to this KeyMap. The following config object properties are supported:
14529      * <pre>
14530 Property    Type             Description
14531 ----------  ---------------  ----------------------------------------------------------------------
14532 key         String/Array     A single keycode or an array of keycodes to handle
14533 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14534 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14535 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14536 fn          Function         The function to call when KeyMap finds the expected key combination
14537 scope       Object           The scope of the callback function
14538 </pre>
14539      *
14540      * Usage:
14541      * <pre><code>
14542 // Create a KeyMap
14543 var map = new Roo.KeyMap(document, {
14544     key: Roo.EventObject.ENTER,
14545     fn: handleKey,
14546     scope: this
14547 });
14548
14549 //Add a new binding to the existing KeyMap later
14550 map.addBinding({
14551     key: 'abc',
14552     shift: true,
14553     fn: handleKey,
14554     scope: this
14555 });
14556 </code></pre>
14557      * @param {Object/Array} config A single KeyMap config or an array of configs
14558      */
14559         addBinding : function(config){
14560         if(config instanceof Array){
14561             for(var i = 0, len = config.length; i < len; i++){
14562                 this.addBinding(config[i]);
14563             }
14564             return;
14565         }
14566         var keyCode = config.key,
14567             shift = config.shift, 
14568             ctrl = config.ctrl, 
14569             alt = config.alt,
14570             fn = config.fn,
14571             scope = config.scope;
14572         if(typeof keyCode == "string"){
14573             var ks = [];
14574             var keyString = keyCode.toUpperCase();
14575             for(var j = 0, len = keyString.length; j < len; j++){
14576                 ks.push(keyString.charCodeAt(j));
14577             }
14578             keyCode = ks;
14579         }
14580         var keyArray = keyCode instanceof Array;
14581         var handler = function(e){
14582             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14583                 var k = e.getKey();
14584                 if(keyArray){
14585                     for(var i = 0, len = keyCode.length; i < len; i++){
14586                         if(keyCode[i] == k){
14587                           if(this.stopEvent){
14588                               e.stopEvent();
14589                           }
14590                           fn.call(scope || window, k, e);
14591                           return;
14592                         }
14593                     }
14594                 }else{
14595                     if(k == keyCode){
14596                         if(this.stopEvent){
14597                            e.stopEvent();
14598                         }
14599                         fn.call(scope || window, k, e);
14600                     }
14601                 }
14602             }
14603         };
14604         this.bindings.push(handler);  
14605         },
14606
14607     /**
14608      * Shorthand for adding a single key listener
14609      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14610      * following options:
14611      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14612      * @param {Function} fn The function to call
14613      * @param {Object} scope (optional) The scope of the function
14614      */
14615     on : function(key, fn, scope){
14616         var keyCode, shift, ctrl, alt;
14617         if(typeof key == "object" && !(key instanceof Array)){
14618             keyCode = key.key;
14619             shift = key.shift;
14620             ctrl = key.ctrl;
14621             alt = key.alt;
14622         }else{
14623             keyCode = key;
14624         }
14625         this.addBinding({
14626             key: keyCode,
14627             shift: shift,
14628             ctrl: ctrl,
14629             alt: alt,
14630             fn: fn,
14631             scope: scope
14632         })
14633     },
14634
14635     // private
14636     handleKeyDown : function(e){
14637             if(this.enabled){ //just in case
14638             var b = this.bindings;
14639             for(var i = 0, len = b.length; i < len; i++){
14640                 b[i].call(this, e);
14641             }
14642             }
14643         },
14644         
14645         /**
14646          * Returns true if this KeyMap is enabled
14647          * @return {Boolean} 
14648          */
14649         isEnabled : function(){
14650             return this.enabled;  
14651         },
14652         
14653         /**
14654          * Enables this KeyMap
14655          */
14656         enable: function(){
14657                 if(!this.enabled){
14658                     this.el.on(this.eventName, this.handleKeyDown, this);
14659                     this.enabled = true;
14660                 }
14661         },
14662
14663         /**
14664          * Disable this KeyMap
14665          */
14666         disable: function(){
14667                 if(this.enabled){
14668                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14669                     this.enabled = false;
14670                 }
14671         }
14672 };/*
14673  * Based on:
14674  * Ext JS Library 1.1.1
14675  * Copyright(c) 2006-2007, Ext JS, LLC.
14676  *
14677  * Originally Released Under LGPL - original licence link has changed is not relivant.
14678  *
14679  * Fork - LGPL
14680  * <script type="text/javascript">
14681  */
14682
14683  
14684 /**
14685  * @class Roo.util.TextMetrics
14686  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14687  * wide, in pixels, a given block of text will be.
14688  * @singleton
14689  */
14690 Roo.util.TextMetrics = function(){
14691     var shared;
14692     return {
14693         /**
14694          * Measures the size of the specified text
14695          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14696          * that can affect the size of the rendered text
14697          * @param {String} text The text to measure
14698          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14699          * in order to accurately measure the text height
14700          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14701          */
14702         measure : function(el, text, fixedWidth){
14703             if(!shared){
14704                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14705             }
14706             shared.bind(el);
14707             shared.setFixedWidth(fixedWidth || 'auto');
14708             return shared.getSize(text);
14709         },
14710
14711         /**
14712          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14713          * the overhead of multiple calls to initialize the style properties on each measurement.
14714          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14715          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14716          * in order to accurately measure the text height
14717          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14718          */
14719         createInstance : function(el, fixedWidth){
14720             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14721         }
14722     };
14723 }();
14724
14725  
14726
14727 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14728     var ml = new Roo.Element(document.createElement('div'));
14729     document.body.appendChild(ml.dom);
14730     ml.position('absolute');
14731     ml.setLeftTop(-1000, -1000);
14732     ml.hide();
14733
14734     if(fixedWidth){
14735         ml.setWidth(fixedWidth);
14736     }
14737      
14738     var instance = {
14739         /**
14740          * Returns the size of the specified text based on the internal element's style and width properties
14741          * @memberOf Roo.util.TextMetrics.Instance#
14742          * @param {String} text The text to measure
14743          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14744          */
14745         getSize : function(text){
14746             ml.update(text);
14747             var s = ml.getSize();
14748             ml.update('');
14749             return s;
14750         },
14751
14752         /**
14753          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14754          * that can affect the size of the rendered text
14755          * @memberOf Roo.util.TextMetrics.Instance#
14756          * @param {String/HTMLElement} el The element, dom node or id
14757          */
14758         bind : function(el){
14759             ml.setStyle(
14760                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14761             );
14762         },
14763
14764         /**
14765          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14766          * to set a fixed width in order to accurately measure the text height.
14767          * @memberOf Roo.util.TextMetrics.Instance#
14768          * @param {Number} width The width to set on the element
14769          */
14770         setFixedWidth : function(width){
14771             ml.setWidth(width);
14772         },
14773
14774         /**
14775          * Returns the measured width of the specified text
14776          * @memberOf Roo.util.TextMetrics.Instance#
14777          * @param {String} text The text to measure
14778          * @return {Number} width The width in pixels
14779          */
14780         getWidth : function(text){
14781             ml.dom.style.width = 'auto';
14782             return this.getSize(text).width;
14783         },
14784
14785         /**
14786          * Returns the measured height of the specified text.  For multiline text, be sure to call
14787          * {@link #setFixedWidth} if necessary.
14788          * @memberOf Roo.util.TextMetrics.Instance#
14789          * @param {String} text The text to measure
14790          * @return {Number} height The height in pixels
14791          */
14792         getHeight : function(text){
14793             return this.getSize(text).height;
14794         }
14795     };
14796
14797     instance.bind(bindTo);
14798
14799     return instance;
14800 };
14801
14802 // backwards compat
14803 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14804  * Based on:
14805  * Ext JS Library 1.1.1
14806  * Copyright(c) 2006-2007, Ext JS, LLC.
14807  *
14808  * Originally Released Under LGPL - original licence link has changed is not relivant.
14809  *
14810  * Fork - LGPL
14811  * <script type="text/javascript">
14812  */
14813
14814 /**
14815  * @class Roo.state.Provider
14816  * Abstract base class for state provider implementations. This class provides methods
14817  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14818  * Provider interface.
14819  */
14820 Roo.state.Provider = function(){
14821     /**
14822      * @event statechange
14823      * Fires when a state change occurs.
14824      * @param {Provider} this This state provider
14825      * @param {String} key The state key which was changed
14826      * @param {String} value The encoded value for the state
14827      */
14828     this.addEvents({
14829         "statechange": true
14830     });
14831     this.state = {};
14832     Roo.state.Provider.superclass.constructor.call(this);
14833 };
14834 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14835     /**
14836      * Returns the current value for a key
14837      * @param {String} name The key name
14838      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14839      * @return {Mixed} The state data
14840      */
14841     get : function(name, defaultValue){
14842         return typeof this.state[name] == "undefined" ?
14843             defaultValue : this.state[name];
14844     },
14845     
14846     /**
14847      * Clears a value from the state
14848      * @param {String} name The key name
14849      */
14850     clear : function(name){
14851         delete this.state[name];
14852         this.fireEvent("statechange", this, name, null);
14853     },
14854     
14855     /**
14856      * Sets the value for a key
14857      * @param {String} name The key name
14858      * @param {Mixed} value The value to set
14859      */
14860     set : function(name, value){
14861         this.state[name] = value;
14862         this.fireEvent("statechange", this, name, value);
14863     },
14864     
14865     /**
14866      * Decodes a string previously encoded with {@link #encodeValue}.
14867      * @param {String} value The value to decode
14868      * @return {Mixed} The decoded value
14869      */
14870     decodeValue : function(cookie){
14871         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14872         var matches = re.exec(unescape(cookie));
14873         if(!matches || !matches[1]) {
14874             return; // non state cookie
14875         }
14876         var type = matches[1];
14877         var v = matches[2];
14878         switch(type){
14879             case "n":
14880                 return parseFloat(v);
14881             case "d":
14882                 return new Date(Date.parse(v));
14883             case "b":
14884                 return (v == "1");
14885             case "a":
14886                 var all = [];
14887                 var values = v.split("^");
14888                 for(var i = 0, len = values.length; i < len; i++){
14889                     all.push(this.decodeValue(values[i]));
14890                 }
14891                 return all;
14892            case "o":
14893                 var all = {};
14894                 var values = v.split("^");
14895                 for(var i = 0, len = values.length; i < len; i++){
14896                     var kv = values[i].split("=");
14897                     all[kv[0]] = this.decodeValue(kv[1]);
14898                 }
14899                 return all;
14900            default:
14901                 return v;
14902         }
14903     },
14904     
14905     /**
14906      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14907      * @param {Mixed} value The value to encode
14908      * @return {String} The encoded value
14909      */
14910     encodeValue : function(v){
14911         var enc;
14912         if(typeof v == "number"){
14913             enc = "n:" + v;
14914         }else if(typeof v == "boolean"){
14915             enc = "b:" + (v ? "1" : "0");
14916         }else if(v instanceof Date){
14917             enc = "d:" + v.toGMTString();
14918         }else if(v instanceof Array){
14919             var flat = "";
14920             for(var i = 0, len = v.length; i < len; i++){
14921                 flat += this.encodeValue(v[i]);
14922                 if(i != len-1) {
14923                     flat += "^";
14924                 }
14925             }
14926             enc = "a:" + flat;
14927         }else if(typeof v == "object"){
14928             var flat = "";
14929             for(var key in v){
14930                 if(typeof v[key] != "function"){
14931                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14932                 }
14933             }
14934             enc = "o:" + flat.substring(0, flat.length-1);
14935         }else{
14936             enc = "s:" + v;
14937         }
14938         return escape(enc);        
14939     }
14940 });
14941
14942 /*
14943  * Based on:
14944  * Ext JS Library 1.1.1
14945  * Copyright(c) 2006-2007, Ext JS, LLC.
14946  *
14947  * Originally Released Under LGPL - original licence link has changed is not relivant.
14948  *
14949  * Fork - LGPL
14950  * <script type="text/javascript">
14951  */
14952 /**
14953  * @class Roo.state.Manager
14954  * This is the global state manager. By default all components that are "state aware" check this class
14955  * for state information if you don't pass them a custom state provider. In order for this class
14956  * to be useful, it must be initialized with a provider when your application initializes.
14957  <pre><code>
14958 // in your initialization function
14959 init : function(){
14960    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14961    ...
14962    // supposed you have a {@link Roo.BorderLayout}
14963    var layout = new Roo.BorderLayout(...);
14964    layout.restoreState();
14965    // or a {Roo.BasicDialog}
14966    var dialog = new Roo.BasicDialog(...);
14967    dialog.restoreState();
14968  </code></pre>
14969  * @singleton
14970  */
14971 Roo.state.Manager = function(){
14972     var provider = new Roo.state.Provider();
14973     
14974     return {
14975         /**
14976          * Configures the default state provider for your application
14977          * @param {Provider} stateProvider The state provider to set
14978          */
14979         setProvider : function(stateProvider){
14980             provider = stateProvider;
14981         },
14982         
14983         /**
14984          * Returns the current value for a key
14985          * @param {String} name The key name
14986          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14987          * @return {Mixed} The state data
14988          */
14989         get : function(key, defaultValue){
14990             return provider.get(key, defaultValue);
14991         },
14992         
14993         /**
14994          * Sets the value for a key
14995          * @param {String} name The key name
14996          * @param {Mixed} value The state data
14997          */
14998          set : function(key, value){
14999             provider.set(key, value);
15000         },
15001         
15002         /**
15003          * Clears a value from the state
15004          * @param {String} name The key name
15005          */
15006         clear : function(key){
15007             provider.clear(key);
15008         },
15009         
15010         /**
15011          * Gets the currently configured state provider
15012          * @return {Provider} The state provider
15013          */
15014         getProvider : function(){
15015             return provider;
15016         }
15017     };
15018 }();
15019 /*
15020  * Based on:
15021  * Ext JS Library 1.1.1
15022  * Copyright(c) 2006-2007, Ext JS, LLC.
15023  *
15024  * Originally Released Under LGPL - original licence link has changed is not relivant.
15025  *
15026  * Fork - LGPL
15027  * <script type="text/javascript">
15028  */
15029 /**
15030  * @class Roo.state.CookieProvider
15031  * @extends Roo.state.Provider
15032  * The default Provider implementation which saves state via cookies.
15033  * <br />Usage:
15034  <pre><code>
15035    var cp = new Roo.state.CookieProvider({
15036        path: "/cgi-bin/",
15037        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15038        domain: "roojs.com"
15039    })
15040    Roo.state.Manager.setProvider(cp);
15041  </code></pre>
15042  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15043  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15044  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15045  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15046  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15047  * domain the page is running on including the 'www' like 'www.roojs.com')
15048  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15049  * @constructor
15050  * Create a new CookieProvider
15051  * @param {Object} config The configuration object
15052  */
15053 Roo.state.CookieProvider = function(config){
15054     Roo.state.CookieProvider.superclass.constructor.call(this);
15055     this.path = "/";
15056     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15057     this.domain = null;
15058     this.secure = false;
15059     Roo.apply(this, config);
15060     this.state = this.readCookies();
15061 };
15062
15063 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15064     // private
15065     set : function(name, value){
15066         if(typeof value == "undefined" || value === null){
15067             this.clear(name);
15068             return;
15069         }
15070         this.setCookie(name, value);
15071         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15072     },
15073
15074     // private
15075     clear : function(name){
15076         this.clearCookie(name);
15077         Roo.state.CookieProvider.superclass.clear.call(this, name);
15078     },
15079
15080     // private
15081     readCookies : function(){
15082         var cookies = {};
15083         var c = document.cookie + ";";
15084         var re = /\s?(.*?)=(.*?);/g;
15085         var matches;
15086         while((matches = re.exec(c)) != null){
15087             var name = matches[1];
15088             var value = matches[2];
15089             if(name && name.substring(0,3) == "ys-"){
15090                 cookies[name.substr(3)] = this.decodeValue(value);
15091             }
15092         }
15093         return cookies;
15094     },
15095
15096     // private
15097     setCookie : function(name, value){
15098         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15099            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15100            ((this.path == null) ? "" : ("; path=" + this.path)) +
15101            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15102            ((this.secure == true) ? "; secure" : "");
15103     },
15104
15105     // private
15106     clearCookie : function(name){
15107         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15108            ((this.path == null) ? "" : ("; path=" + this.path)) +
15109            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15110            ((this.secure == true) ? "; secure" : "");
15111     }
15112 });/*
15113  * Based on:
15114  * Ext JS Library 1.1.1
15115  * Copyright(c) 2006-2007, Ext JS, LLC.
15116  *
15117  * Originally Released Under LGPL - original licence link has changed is not relivant.
15118  *
15119  * Fork - LGPL
15120  * <script type="text/javascript">
15121  */
15122  
15123
15124 /**
15125  * @class Roo.ComponentMgr
15126  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15127  * @singleton
15128  */
15129 Roo.ComponentMgr = function(){
15130     var all = new Roo.util.MixedCollection();
15131
15132     return {
15133         /**
15134          * Registers a component.
15135          * @param {Roo.Component} c The component
15136          */
15137         register : function(c){
15138             all.add(c);
15139         },
15140
15141         /**
15142          * Unregisters a component.
15143          * @param {Roo.Component} c The component
15144          */
15145         unregister : function(c){
15146             all.remove(c);
15147         },
15148
15149         /**
15150          * Returns a component by id
15151          * @param {String} id The component id
15152          */
15153         get : function(id){
15154             return all.get(id);
15155         },
15156
15157         /**
15158          * Registers a function that will be called when a specified component is added to ComponentMgr
15159          * @param {String} id The component id
15160          * @param {Funtction} fn The callback function
15161          * @param {Object} scope The scope of the callback
15162          */
15163         onAvailable : function(id, fn, scope){
15164             all.on("add", function(index, o){
15165                 if(o.id == id){
15166                     fn.call(scope || o, o);
15167                     all.un("add", fn, scope);
15168                 }
15169             });
15170         }
15171     };
15172 }();/*
15173  * Based on:
15174  * Ext JS Library 1.1.1
15175  * Copyright(c) 2006-2007, Ext JS, LLC.
15176  *
15177  * Originally Released Under LGPL - original licence link has changed is not relivant.
15178  *
15179  * Fork - LGPL
15180  * <script type="text/javascript">
15181  */
15182  
15183 /**
15184  * @class Roo.Component
15185  * @extends Roo.util.Observable
15186  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15187  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15188  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15189  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15190  * All visual components (widgets) that require rendering into a layout should subclass Component.
15191  * @constructor
15192  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15193  * 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
15194  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15195  */
15196 Roo.Component = function(config){
15197     config = config || {};
15198     if(config.tagName || config.dom || typeof config == "string"){ // element object
15199         config = {el: config, id: config.id || config};
15200     }
15201     this.initialConfig = config;
15202
15203     Roo.apply(this, config);
15204     this.addEvents({
15205         /**
15206          * @event disable
15207          * Fires after the component is disabled.
15208              * @param {Roo.Component} this
15209              */
15210         disable : true,
15211         /**
15212          * @event enable
15213          * Fires after the component is enabled.
15214              * @param {Roo.Component} this
15215              */
15216         enable : true,
15217         /**
15218          * @event beforeshow
15219          * Fires before the component is shown.  Return false to stop the show.
15220              * @param {Roo.Component} this
15221              */
15222         beforeshow : true,
15223         /**
15224          * @event show
15225          * Fires after the component is shown.
15226              * @param {Roo.Component} this
15227              */
15228         show : true,
15229         /**
15230          * @event beforehide
15231          * Fires before the component is hidden. Return false to stop the hide.
15232              * @param {Roo.Component} this
15233              */
15234         beforehide : true,
15235         /**
15236          * @event hide
15237          * Fires after the component is hidden.
15238              * @param {Roo.Component} this
15239              */
15240         hide : true,
15241         /**
15242          * @event beforerender
15243          * Fires before the component is rendered. Return false to stop the render.
15244              * @param {Roo.Component} this
15245              */
15246         beforerender : true,
15247         /**
15248          * @event render
15249          * Fires after the component is rendered.
15250              * @param {Roo.Component} this
15251              */
15252         render : true,
15253         /**
15254          * @event beforedestroy
15255          * Fires before the component is destroyed. Return false to stop the destroy.
15256              * @param {Roo.Component} this
15257              */
15258         beforedestroy : true,
15259         /**
15260          * @event destroy
15261          * Fires after the component is destroyed.
15262              * @param {Roo.Component} this
15263              */
15264         destroy : true
15265     });
15266     if(!this.id){
15267         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15268     }
15269     Roo.ComponentMgr.register(this);
15270     Roo.Component.superclass.constructor.call(this);
15271     this.initComponent();
15272     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15273         this.render(this.renderTo);
15274         delete this.renderTo;
15275     }
15276 };
15277
15278 /** @private */
15279 Roo.Component.AUTO_ID = 1000;
15280
15281 Roo.extend(Roo.Component, Roo.util.Observable, {
15282     /**
15283      * @scope Roo.Component.prototype
15284      * @type {Boolean}
15285      * true if this component is hidden. Read-only.
15286      */
15287     hidden : false,
15288     /**
15289      * @type {Boolean}
15290      * true if this component is disabled. Read-only.
15291      */
15292     disabled : false,
15293     /**
15294      * @type {Boolean}
15295      * true if this component has been rendered. Read-only.
15296      */
15297     rendered : false,
15298     
15299     /** @cfg {String} disableClass
15300      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15301      */
15302     disabledClass : "x-item-disabled",
15303         /** @cfg {Boolean} allowDomMove
15304          * Whether the component can move the Dom node when rendering (defaults to true).
15305          */
15306     allowDomMove : true,
15307     /** @cfg {String} hideMode (display|visibility)
15308      * How this component should hidden. Supported values are
15309      * "visibility" (css visibility), "offsets" (negative offset position) and
15310      * "display" (css display) - defaults to "display".
15311      */
15312     hideMode: 'display',
15313
15314     /** @private */
15315     ctype : "Roo.Component",
15316
15317     /**
15318      * @cfg {String} actionMode 
15319      * which property holds the element that used for  hide() / show() / disable() / enable()
15320      * default is 'el' 
15321      */
15322     actionMode : "el",
15323
15324     /** @private */
15325     getActionEl : function(){
15326         return this[this.actionMode];
15327     },
15328
15329     initComponent : Roo.emptyFn,
15330     /**
15331      * If this is a lazy rendering component, render it to its container element.
15332      * @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.
15333      */
15334     render : function(container, position){
15335         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15336             if(!container && this.el){
15337                 this.el = Roo.get(this.el);
15338                 container = this.el.dom.parentNode;
15339                 this.allowDomMove = false;
15340             }
15341             this.container = Roo.get(container);
15342             this.rendered = true;
15343             if(position !== undefined){
15344                 if(typeof position == 'number'){
15345                     position = this.container.dom.childNodes[position];
15346                 }else{
15347                     position = Roo.getDom(position);
15348                 }
15349             }
15350             this.onRender(this.container, position || null);
15351             if(this.cls){
15352                 this.el.addClass(this.cls);
15353                 delete this.cls;
15354             }
15355             if(this.style){
15356                 this.el.applyStyles(this.style);
15357                 delete this.style;
15358             }
15359             this.fireEvent("render", this);
15360             this.afterRender(this.container);
15361             if(this.hidden){
15362                 this.hide();
15363             }
15364             if(this.disabled){
15365                 this.disable();
15366             }
15367         }
15368         return this;
15369     },
15370
15371     /** @private */
15372     // default function is not really useful
15373     onRender : function(ct, position){
15374         if(this.el){
15375             this.el = Roo.get(this.el);
15376             if(this.allowDomMove !== false){
15377                 ct.dom.insertBefore(this.el.dom, position);
15378             }
15379         }
15380     },
15381
15382     /** @private */
15383     getAutoCreate : function(){
15384         var cfg = typeof this.autoCreate == "object" ?
15385                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15386         if(this.id && !cfg.id){
15387             cfg.id = this.id;
15388         }
15389         return cfg;
15390     },
15391
15392     /** @private */
15393     afterRender : Roo.emptyFn,
15394
15395     /**
15396      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15397      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15398      */
15399     destroy : function(){
15400         if(this.fireEvent("beforedestroy", this) !== false){
15401             this.purgeListeners();
15402             this.beforeDestroy();
15403             if(this.rendered){
15404                 this.el.removeAllListeners();
15405                 this.el.remove();
15406                 if(this.actionMode == "container"){
15407                     this.container.remove();
15408                 }
15409             }
15410             this.onDestroy();
15411             Roo.ComponentMgr.unregister(this);
15412             this.fireEvent("destroy", this);
15413         }
15414     },
15415
15416         /** @private */
15417     beforeDestroy : function(){
15418
15419     },
15420
15421         /** @private */
15422         onDestroy : function(){
15423
15424     },
15425
15426     /**
15427      * Returns the underlying {@link Roo.Element}.
15428      * @return {Roo.Element} The element
15429      */
15430     getEl : function(){
15431         return this.el;
15432     },
15433
15434     /**
15435      * Returns the id of this component.
15436      * @return {String}
15437      */
15438     getId : function(){
15439         return this.id;
15440     },
15441
15442     /**
15443      * Try to focus this component.
15444      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15445      * @return {Roo.Component} this
15446      */
15447     focus : function(selectText){
15448         if(this.rendered){
15449             this.el.focus();
15450             if(selectText === true){
15451                 this.el.dom.select();
15452             }
15453         }
15454         return this;
15455     },
15456
15457     /** @private */
15458     blur : function(){
15459         if(this.rendered){
15460             this.el.blur();
15461         }
15462         return this;
15463     },
15464
15465     /**
15466      * Disable this component.
15467      * @return {Roo.Component} this
15468      */
15469     disable : function(){
15470         if(this.rendered){
15471             this.onDisable();
15472         }
15473         this.disabled = true;
15474         this.fireEvent("disable", this);
15475         return this;
15476     },
15477
15478         // private
15479     onDisable : function(){
15480         this.getActionEl().addClass(this.disabledClass);
15481         this.el.dom.disabled = true;
15482     },
15483
15484     /**
15485      * Enable this component.
15486      * @return {Roo.Component} this
15487      */
15488     enable : function(){
15489         if(this.rendered){
15490             this.onEnable();
15491         }
15492         this.disabled = false;
15493         this.fireEvent("enable", this);
15494         return this;
15495     },
15496
15497         // private
15498     onEnable : function(){
15499         this.getActionEl().removeClass(this.disabledClass);
15500         this.el.dom.disabled = false;
15501     },
15502
15503     /**
15504      * Convenience function for setting disabled/enabled by boolean.
15505      * @param {Boolean} disabled
15506      */
15507     setDisabled : function(disabled){
15508         this[disabled ? "disable" : "enable"]();
15509     },
15510
15511     /**
15512      * Show this component.
15513      * @return {Roo.Component} this
15514      */
15515     show: function(){
15516         if(this.fireEvent("beforeshow", this) !== false){
15517             this.hidden = false;
15518             if(this.rendered){
15519                 this.onShow();
15520             }
15521             this.fireEvent("show", this);
15522         }
15523         return this;
15524     },
15525
15526     // private
15527     onShow : function(){
15528         var ae = this.getActionEl();
15529         if(this.hideMode == 'visibility'){
15530             ae.dom.style.visibility = "visible";
15531         }else if(this.hideMode == 'offsets'){
15532             ae.removeClass('x-hidden');
15533         }else{
15534             ae.dom.style.display = "";
15535         }
15536     },
15537
15538     /**
15539      * Hide this component.
15540      * @return {Roo.Component} this
15541      */
15542     hide: function(){
15543         if(this.fireEvent("beforehide", this) !== false){
15544             this.hidden = true;
15545             if(this.rendered){
15546                 this.onHide();
15547             }
15548             this.fireEvent("hide", this);
15549         }
15550         return this;
15551     },
15552
15553     // private
15554     onHide : function(){
15555         var ae = this.getActionEl();
15556         if(this.hideMode == 'visibility'){
15557             ae.dom.style.visibility = "hidden";
15558         }else if(this.hideMode == 'offsets'){
15559             ae.addClass('x-hidden');
15560         }else{
15561             ae.dom.style.display = "none";
15562         }
15563     },
15564
15565     /**
15566      * Convenience function to hide or show this component by boolean.
15567      * @param {Boolean} visible True to show, false to hide
15568      * @return {Roo.Component} this
15569      */
15570     setVisible: function(visible){
15571         if(visible) {
15572             this.show();
15573         }else{
15574             this.hide();
15575         }
15576         return this;
15577     },
15578
15579     /**
15580      * Returns true if this component is visible.
15581      */
15582     isVisible : function(){
15583         return this.getActionEl().isVisible();
15584     },
15585
15586     cloneConfig : function(overrides){
15587         overrides = overrides || {};
15588         var id = overrides.id || Roo.id();
15589         var cfg = Roo.applyIf(overrides, this.initialConfig);
15590         cfg.id = id; // prevent dup id
15591         return new this.constructor(cfg);
15592     }
15593 });/*
15594  * Based on:
15595  * Ext JS Library 1.1.1
15596  * Copyright(c) 2006-2007, Ext JS, LLC.
15597  *
15598  * Originally Released Under LGPL - original licence link has changed is not relivant.
15599  *
15600  * Fork - LGPL
15601  * <script type="text/javascript">
15602  */
15603
15604 /**
15605  * @class Roo.BoxComponent
15606  * @extends Roo.Component
15607  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15608  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15609  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15610  * layout containers.
15611  * @constructor
15612  * @param {Roo.Element/String/Object} config The configuration options.
15613  */
15614 Roo.BoxComponent = function(config){
15615     Roo.Component.call(this, config);
15616     this.addEvents({
15617         /**
15618          * @event resize
15619          * Fires after the component is resized.
15620              * @param {Roo.Component} this
15621              * @param {Number} adjWidth The box-adjusted width that was set
15622              * @param {Number} adjHeight The box-adjusted height that was set
15623              * @param {Number} rawWidth The width that was originally specified
15624              * @param {Number} rawHeight The height that was originally specified
15625              */
15626         resize : true,
15627         /**
15628          * @event move
15629          * Fires after the component is moved.
15630              * @param {Roo.Component} this
15631              * @param {Number} x The new x position
15632              * @param {Number} y The new y position
15633              */
15634         move : true
15635     });
15636 };
15637
15638 Roo.extend(Roo.BoxComponent, Roo.Component, {
15639     // private, set in afterRender to signify that the component has been rendered
15640     boxReady : false,
15641     // private, used to defer height settings to subclasses
15642     deferHeight: false,
15643     /** @cfg {Number} width
15644      * width (optional) size of component
15645      */
15646      /** @cfg {Number} height
15647      * height (optional) size of component
15648      */
15649      
15650     /**
15651      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15652      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15653      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15654      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15655      * @return {Roo.BoxComponent} this
15656      */
15657     setSize : function(w, h){
15658         // support for standard size objects
15659         if(typeof w == 'object'){
15660             h = w.height;
15661             w = w.width;
15662         }
15663         // not rendered
15664         if(!this.boxReady){
15665             this.width = w;
15666             this.height = h;
15667             return this;
15668         }
15669
15670         // prevent recalcs when not needed
15671         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15672             return this;
15673         }
15674         this.lastSize = {width: w, height: h};
15675
15676         var adj = this.adjustSize(w, h);
15677         var aw = adj.width, ah = adj.height;
15678         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15679             var rz = this.getResizeEl();
15680             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15681                 rz.setSize(aw, ah);
15682             }else if(!this.deferHeight && ah !== undefined){
15683                 rz.setHeight(ah);
15684             }else if(aw !== undefined){
15685                 rz.setWidth(aw);
15686             }
15687             this.onResize(aw, ah, w, h);
15688             this.fireEvent('resize', this, aw, ah, w, h);
15689         }
15690         return this;
15691     },
15692
15693     /**
15694      * Gets the current size of the component's underlying element.
15695      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15696      */
15697     getSize : function(){
15698         return this.el.getSize();
15699     },
15700
15701     /**
15702      * Gets the current XY position of the component's underlying element.
15703      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15704      * @return {Array} The XY position of the element (e.g., [100, 200])
15705      */
15706     getPosition : function(local){
15707         if(local === true){
15708             return [this.el.getLeft(true), this.el.getTop(true)];
15709         }
15710         return this.xy || this.el.getXY();
15711     },
15712
15713     /**
15714      * Gets the current box measurements of the component's underlying element.
15715      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15716      * @returns {Object} box An object in the format {x, y, width, height}
15717      */
15718     getBox : function(local){
15719         var s = this.el.getSize();
15720         if(local){
15721             s.x = this.el.getLeft(true);
15722             s.y = this.el.getTop(true);
15723         }else{
15724             var xy = this.xy || this.el.getXY();
15725             s.x = xy[0];
15726             s.y = xy[1];
15727         }
15728         return s;
15729     },
15730
15731     /**
15732      * Sets the current box measurements of the component's underlying element.
15733      * @param {Object} box An object in the format {x, y, width, height}
15734      * @returns {Roo.BoxComponent} this
15735      */
15736     updateBox : function(box){
15737         this.setSize(box.width, box.height);
15738         this.setPagePosition(box.x, box.y);
15739         return this;
15740     },
15741
15742     // protected
15743     getResizeEl : function(){
15744         return this.resizeEl || this.el;
15745     },
15746
15747     // protected
15748     getPositionEl : function(){
15749         return this.positionEl || this.el;
15750     },
15751
15752     /**
15753      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15754      * This method fires the move event.
15755      * @param {Number} left The new left
15756      * @param {Number} top The new top
15757      * @returns {Roo.BoxComponent} this
15758      */
15759     setPosition : function(x, y){
15760         this.x = x;
15761         this.y = y;
15762         if(!this.boxReady){
15763             return this;
15764         }
15765         var adj = this.adjustPosition(x, y);
15766         var ax = adj.x, ay = adj.y;
15767
15768         var el = this.getPositionEl();
15769         if(ax !== undefined || ay !== undefined){
15770             if(ax !== undefined && ay !== undefined){
15771                 el.setLeftTop(ax, ay);
15772             }else if(ax !== undefined){
15773                 el.setLeft(ax);
15774             }else if(ay !== undefined){
15775                 el.setTop(ay);
15776             }
15777             this.onPosition(ax, ay);
15778             this.fireEvent('move', this, ax, ay);
15779         }
15780         return this;
15781     },
15782
15783     /**
15784      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15785      * This method fires the move event.
15786      * @param {Number} x The new x position
15787      * @param {Number} y The new y position
15788      * @returns {Roo.BoxComponent} this
15789      */
15790     setPagePosition : function(x, y){
15791         this.pageX = x;
15792         this.pageY = y;
15793         if(!this.boxReady){
15794             return;
15795         }
15796         if(x === undefined || y === undefined){ // cannot translate undefined points
15797             return;
15798         }
15799         var p = this.el.translatePoints(x, y);
15800         this.setPosition(p.left, p.top);
15801         return this;
15802     },
15803
15804     // private
15805     onRender : function(ct, position){
15806         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15807         if(this.resizeEl){
15808             this.resizeEl = Roo.get(this.resizeEl);
15809         }
15810         if(this.positionEl){
15811             this.positionEl = Roo.get(this.positionEl);
15812         }
15813     },
15814
15815     // private
15816     afterRender : function(){
15817         Roo.BoxComponent.superclass.afterRender.call(this);
15818         this.boxReady = true;
15819         this.setSize(this.width, this.height);
15820         if(this.x || this.y){
15821             this.setPosition(this.x, this.y);
15822         }
15823         if(this.pageX || this.pageY){
15824             this.setPagePosition(this.pageX, this.pageY);
15825         }
15826     },
15827
15828     /**
15829      * Force the component's size to recalculate based on the underlying element's current height and width.
15830      * @returns {Roo.BoxComponent} this
15831      */
15832     syncSize : function(){
15833         delete this.lastSize;
15834         this.setSize(this.el.getWidth(), this.el.getHeight());
15835         return this;
15836     },
15837
15838     /**
15839      * Called after the component is resized, this method is empty by default but can be implemented by any
15840      * subclass that needs to perform custom logic after a resize occurs.
15841      * @param {Number} adjWidth The box-adjusted width that was set
15842      * @param {Number} adjHeight The box-adjusted height that was set
15843      * @param {Number} rawWidth The width that was originally specified
15844      * @param {Number} rawHeight The height that was originally specified
15845      */
15846     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15847
15848     },
15849
15850     /**
15851      * Called after the component is moved, this method is empty by default but can be implemented by any
15852      * subclass that needs to perform custom logic after a move occurs.
15853      * @param {Number} x The new x position
15854      * @param {Number} y The new y position
15855      */
15856     onPosition : function(x, y){
15857
15858     },
15859
15860     // private
15861     adjustSize : function(w, h){
15862         if(this.autoWidth){
15863             w = 'auto';
15864         }
15865         if(this.autoHeight){
15866             h = 'auto';
15867         }
15868         return {width : w, height: h};
15869     },
15870
15871     // private
15872     adjustPosition : function(x, y){
15873         return {x : x, y: y};
15874     }
15875 });/*
15876  * Original code for Roojs - LGPL
15877  * <script type="text/javascript">
15878  */
15879  
15880 /**
15881  * @class Roo.XComponent
15882  * A delayed Element creator...
15883  * Or a way to group chunks of interface together.
15884  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15885  *  used in conjunction with XComponent.build() it will create an instance of each element,
15886  *  then call addxtype() to build the User interface.
15887  * 
15888  * Mypart.xyx = new Roo.XComponent({
15889
15890     parent : 'Mypart.xyz', // empty == document.element.!!
15891     order : '001',
15892     name : 'xxxx'
15893     region : 'xxxx'
15894     disabled : function() {} 
15895      
15896     tree : function() { // return an tree of xtype declared components
15897         var MODULE = this;
15898         return 
15899         {
15900             xtype : 'NestedLayoutPanel',
15901             // technicall
15902         }
15903      ]
15904  *})
15905  *
15906  *
15907  * It can be used to build a big heiracy, with parent etc.
15908  * or you can just use this to render a single compoent to a dom element
15909  * MYPART.render(Roo.Element | String(id) | dom_element )
15910  *
15911  *
15912  * Usage patterns.
15913  *
15914  * Classic Roo
15915  *
15916  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15917  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15918  *
15919  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15920  *
15921  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15922  * - if mulitple topModules exist, the last one is defined as the top module.
15923  *
15924  * Embeded Roo
15925  * 
15926  * When the top level or multiple modules are to embedded into a existing HTML page,
15927  * the parent element can container '#id' of the element where the module will be drawn.
15928  *
15929  * Bootstrap Roo
15930  *
15931  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15932  * it relies more on a include mechanism, where sub modules are included into an outer page.
15933  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15934  * 
15935  * Bootstrap Roo Included elements
15936  *
15937  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15938  * hence confusing the component builder as it thinks there are multiple top level elements. 
15939  *
15940  * 
15941  * 
15942  * @extends Roo.util.Observable
15943  * @constructor
15944  * @param cfg {Object} configuration of component
15945  * 
15946  */
15947 Roo.XComponent = function(cfg) {
15948     Roo.apply(this, cfg);
15949     this.addEvents({ 
15950         /**
15951              * @event built
15952              * Fires when this the componnt is built
15953              * @param {Roo.XComponent} c the component
15954              */
15955         'built' : true
15956         
15957     });
15958     this.region = this.region || 'center'; // default..
15959     Roo.XComponent.register(this);
15960     this.modules = false;
15961     this.el = false; // where the layout goes..
15962     
15963     
15964 }
15965 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15966     /**
15967      * @property el
15968      * The created element (with Roo.factory())
15969      * @type {Roo.Layout}
15970      */
15971     el  : false,
15972     
15973     /**
15974      * @property el
15975      * for BC  - use el in new code
15976      * @type {Roo.Layout}
15977      */
15978     panel : false,
15979     
15980     /**
15981      * @property layout
15982      * for BC  - use el in new code
15983      * @type {Roo.Layout}
15984      */
15985     layout : false,
15986     
15987      /**
15988      * @cfg {Function|boolean} disabled
15989      * If this module is disabled by some rule, return true from the funtion
15990      */
15991     disabled : false,
15992     
15993     /**
15994      * @cfg {String} parent 
15995      * Name of parent element which it get xtype added to..
15996      */
15997     parent: false,
15998     
15999     /**
16000      * @cfg {String} order
16001      * Used to set the order in which elements are created (usefull for multiple tabs)
16002      */
16003     
16004     order : false,
16005     /**
16006      * @cfg {String} name
16007      * String to display while loading.
16008      */
16009     name : false,
16010     /**
16011      * @cfg {String} region
16012      * Region to render component to (defaults to center)
16013      */
16014     region : 'center',
16015     
16016     /**
16017      * @cfg {Array} items
16018      * A single item array - the first element is the root of the tree..
16019      * It's done this way to stay compatible with the Xtype system...
16020      */
16021     items : false,
16022     
16023     /**
16024      * @property _tree
16025      * The method that retuns the tree of parts that make up this compoennt 
16026      * @type {function}
16027      */
16028     _tree  : false,
16029     
16030      /**
16031      * render
16032      * render element to dom or tree
16033      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16034      */
16035     
16036     render : function(el)
16037     {
16038         
16039         el = el || false;
16040         var hp = this.parent ? 1 : 0;
16041         Roo.debug &&  Roo.log(this);
16042         
16043         var tree = this._tree ? this._tree() : this.tree();
16044
16045         
16046         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16047             // if parent is a '#.....' string, then let's use that..
16048             var ename = this.parent.substr(1);
16049             this.parent = false;
16050             Roo.debug && Roo.log(ename);
16051             switch (ename) {
16052                 case 'bootstrap-body':
16053                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16054                         // this is the BorderLayout standard?
16055                        this.parent = { el : true };
16056                        break;
16057                     }
16058                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16059                         // need to insert stuff...
16060                         this.parent =  {
16061                              el : new Roo.bootstrap.layout.Border({
16062                                  el : document.body, 
16063                      
16064                                  center: {
16065                                     titlebar: false,
16066                                     autoScroll:false,
16067                                     closeOnTab: true,
16068                                     tabPosition: 'top',
16069                                       //resizeTabs: true,
16070                                     alwaysShowTabs: true,
16071                                     hideTabs: false
16072                                      //minTabWidth: 140
16073                                  }
16074                              })
16075                         
16076                          };
16077                          break;
16078                     }
16079                          
16080                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16081                         this.parent = { el :  new  Roo.bootstrap.Body() };
16082                         Roo.debug && Roo.log("setting el to doc body");
16083                          
16084                     } else {
16085                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16086                     }
16087                     break;
16088                 case 'bootstrap':
16089                     this.parent = { el : true};
16090                     // fall through
16091                 default:
16092                     el = Roo.get(ename);
16093                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16094                         this.parent = { el : true};
16095                     }
16096                     
16097                     break;
16098             }
16099                 
16100             
16101             if (!el && !this.parent) {
16102                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16103                 return;
16104             }
16105         }
16106         
16107         Roo.debug && Roo.log("EL:");
16108         Roo.debug && Roo.log(el);
16109         Roo.debug && Roo.log("this.parent.el:");
16110         Roo.debug && Roo.log(this.parent.el);
16111         
16112
16113         // altertive root elements ??? - we need a better way to indicate these.
16114         var is_alt = Roo.XComponent.is_alt ||
16115                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16116                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16117                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16118         
16119         
16120         
16121         if (!this.parent && is_alt) {
16122             //el = Roo.get(document.body);
16123             this.parent = { el : true };
16124         }
16125             
16126             
16127         
16128         if (!this.parent) {
16129             
16130             Roo.debug && Roo.log("no parent - creating one");
16131             
16132             el = el ? Roo.get(el) : false;      
16133             
16134             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16135                 
16136                 this.parent =  {
16137                     el : new Roo.bootstrap.layout.Border({
16138                         el: el || document.body,
16139                     
16140                         center: {
16141                             titlebar: false,
16142                             autoScroll:false,
16143                             closeOnTab: true,
16144                             tabPosition: 'top',
16145                              //resizeTabs: true,
16146                             alwaysShowTabs: false,
16147                             hideTabs: true,
16148                             minTabWidth: 140,
16149                             overflow: 'visible'
16150                          }
16151                      })
16152                 };
16153             } else {
16154             
16155                 // it's a top level one..
16156                 this.parent =  {
16157                     el : new Roo.BorderLayout(el || document.body, {
16158                         center: {
16159                             titlebar: false,
16160                             autoScroll:false,
16161                             closeOnTab: true,
16162                             tabPosition: 'top',
16163                              //resizeTabs: true,
16164                             alwaysShowTabs: el && hp? false :  true,
16165                             hideTabs: el || !hp ? true :  false,
16166                             minTabWidth: 140
16167                          }
16168                     })
16169                 };
16170             }
16171         }
16172         
16173         if (!this.parent.el) {
16174                 // probably an old style ctor, which has been disabled.
16175                 return;
16176
16177         }
16178                 // The 'tree' method is  '_tree now' 
16179             
16180         tree.region = tree.region || this.region;
16181         var is_body = false;
16182         if (this.parent.el === true) {
16183             // bootstrap... - body..
16184             if (el) {
16185                 tree.el = el;
16186             }
16187             this.parent.el = Roo.factory(tree);
16188             is_body = true;
16189         }
16190         
16191         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16192         this.fireEvent('built', this);
16193         
16194         this.panel = this.el;
16195         this.layout = this.panel.layout;
16196         this.parentLayout = this.parent.layout  || false;  
16197          
16198     }
16199     
16200 });
16201
16202 Roo.apply(Roo.XComponent, {
16203     /**
16204      * @property  hideProgress
16205      * true to disable the building progress bar.. usefull on single page renders.
16206      * @type Boolean
16207      */
16208     hideProgress : false,
16209     /**
16210      * @property  buildCompleted
16211      * True when the builder has completed building the interface.
16212      * @type Boolean
16213      */
16214     buildCompleted : false,
16215      
16216     /**
16217      * @property  topModule
16218      * the upper most module - uses document.element as it's constructor.
16219      * @type Object
16220      */
16221      
16222     topModule  : false,
16223       
16224     /**
16225      * @property  modules
16226      * array of modules to be created by registration system.
16227      * @type {Array} of Roo.XComponent
16228      */
16229     
16230     modules : [],
16231     /**
16232      * @property  elmodules
16233      * array of modules to be created by which use #ID 
16234      * @type {Array} of Roo.XComponent
16235      */
16236      
16237     elmodules : [],
16238
16239      /**
16240      * @property  is_alt
16241      * Is an alternative Root - normally used by bootstrap or other systems,
16242      *    where the top element in the tree can wrap 'body' 
16243      * @type {boolean}  (default false)
16244      */
16245      
16246     is_alt : false,
16247     /**
16248      * @property  build_from_html
16249      * Build elements from html - used by bootstrap HTML stuff 
16250      *    - this is cleared after build is completed
16251      * @type {boolean}    (default false)
16252      */
16253      
16254     build_from_html : false,
16255     /**
16256      * Register components to be built later.
16257      *
16258      * This solves the following issues
16259      * - Building is not done on page load, but after an authentication process has occured.
16260      * - Interface elements are registered on page load
16261      * - Parent Interface elements may not be loaded before child, so this handles that..
16262      * 
16263      *
16264      * example:
16265      * 
16266      * MyApp.register({
16267           order : '000001',
16268           module : 'Pman.Tab.projectMgr',
16269           region : 'center',
16270           parent : 'Pman.layout',
16271           disabled : false,  // or use a function..
16272         })
16273      
16274      * * @param {Object} details about module
16275      */
16276     register : function(obj) {
16277                 
16278         Roo.XComponent.event.fireEvent('register', obj);
16279         switch(typeof(obj.disabled) ) {
16280                 
16281             case 'undefined':
16282                 break;
16283             
16284             case 'function':
16285                 if ( obj.disabled() ) {
16286                         return;
16287                 }
16288                 break;
16289             
16290             default:
16291                 if (obj.disabled) {
16292                         return;
16293                 }
16294                 break;
16295         }
16296                 
16297         this.modules.push(obj);
16298          
16299     },
16300     /**
16301      * convert a string to an object..
16302      * eg. 'AAA.BBB' -> finds AAA.BBB
16303
16304      */
16305     
16306     toObject : function(str)
16307     {
16308         if (!str || typeof(str) == 'object') {
16309             return str;
16310         }
16311         if (str.substring(0,1) == '#') {
16312             return str;
16313         }
16314
16315         var ar = str.split('.');
16316         var rt, o;
16317         rt = ar.shift();
16318             /** eval:var:o */
16319         try {
16320             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16321         } catch (e) {
16322             throw "Module not found : " + str;
16323         }
16324         
16325         if (o === false) {
16326             throw "Module not found : " + str;
16327         }
16328         Roo.each(ar, function(e) {
16329             if (typeof(o[e]) == 'undefined') {
16330                 throw "Module not found : " + str;
16331             }
16332             o = o[e];
16333         });
16334         
16335         return o;
16336         
16337     },
16338     
16339     
16340     /**
16341      * move modules into their correct place in the tree..
16342      * 
16343      */
16344     preBuild : function ()
16345     {
16346         var _t = this;
16347         Roo.each(this.modules , function (obj)
16348         {
16349             Roo.XComponent.event.fireEvent('beforebuild', obj);
16350             
16351             var opar = obj.parent;
16352             try { 
16353                 obj.parent = this.toObject(opar);
16354             } catch(e) {
16355                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16356                 return;
16357             }
16358             
16359             if (!obj.parent) {
16360                 Roo.debug && Roo.log("GOT top level module");
16361                 Roo.debug && Roo.log(obj);
16362                 obj.modules = new Roo.util.MixedCollection(false, 
16363                     function(o) { return o.order + '' }
16364                 );
16365                 this.topModule = obj;
16366                 return;
16367             }
16368                         // parent is a string (usually a dom element name..)
16369             if (typeof(obj.parent) == 'string') {
16370                 this.elmodules.push(obj);
16371                 return;
16372             }
16373             if (obj.parent.constructor != Roo.XComponent) {
16374                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16375             }
16376             if (!obj.parent.modules) {
16377                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16378                     function(o) { return o.order + '' }
16379                 );
16380             }
16381             if (obj.parent.disabled) {
16382                 obj.disabled = true;
16383             }
16384             obj.parent.modules.add(obj);
16385         }, this);
16386     },
16387     
16388      /**
16389      * make a list of modules to build.
16390      * @return {Array} list of modules. 
16391      */ 
16392     
16393     buildOrder : function()
16394     {
16395         var _this = this;
16396         var cmp = function(a,b) {   
16397             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16398         };
16399         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16400             throw "No top level modules to build";
16401         }
16402         
16403         // make a flat list in order of modules to build.
16404         var mods = this.topModule ? [ this.topModule ] : [];
16405                 
16406         
16407         // elmodules (is a list of DOM based modules )
16408         Roo.each(this.elmodules, function(e) {
16409             mods.push(e);
16410             if (!this.topModule &&
16411                 typeof(e.parent) == 'string' &&
16412                 e.parent.substring(0,1) == '#' &&
16413                 Roo.get(e.parent.substr(1))
16414                ) {
16415                 
16416                 _this.topModule = e;
16417             }
16418             
16419         });
16420
16421         
16422         // add modules to their parents..
16423         var addMod = function(m) {
16424             Roo.debug && Roo.log("build Order: add: " + m.name);
16425                 
16426             mods.push(m);
16427             if (m.modules && !m.disabled) {
16428                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16429                 m.modules.keySort('ASC',  cmp );
16430                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16431     
16432                 m.modules.each(addMod);
16433             } else {
16434                 Roo.debug && Roo.log("build Order: no child modules");
16435             }
16436             // not sure if this is used any more..
16437             if (m.finalize) {
16438                 m.finalize.name = m.name + " (clean up) ";
16439                 mods.push(m.finalize);
16440             }
16441             
16442         }
16443         if (this.topModule && this.topModule.modules) { 
16444             this.topModule.modules.keySort('ASC',  cmp );
16445             this.topModule.modules.each(addMod);
16446         } 
16447         return mods;
16448     },
16449     
16450      /**
16451      * Build the registered modules.
16452      * @param {Object} parent element.
16453      * @param {Function} optional method to call after module has been added.
16454      * 
16455      */ 
16456    
16457     build : function(opts) 
16458     {
16459         
16460         if (typeof(opts) != 'undefined') {
16461             Roo.apply(this,opts);
16462         }
16463         
16464         this.preBuild();
16465         var mods = this.buildOrder();
16466       
16467         //this.allmods = mods;
16468         //Roo.debug && Roo.log(mods);
16469         //return;
16470         if (!mods.length) { // should not happen
16471             throw "NO modules!!!";
16472         }
16473         
16474         
16475         var msg = "Building Interface...";
16476         // flash it up as modal - so we store the mask!?
16477         if (!this.hideProgress && Roo.MessageBox) {
16478             Roo.MessageBox.show({ title: 'loading' });
16479             Roo.MessageBox.show({
16480                title: "Please wait...",
16481                msg: msg,
16482                width:450,
16483                progress:true,
16484                closable:false,
16485                modal: false
16486               
16487             });
16488         }
16489         var total = mods.length;
16490         
16491         var _this = this;
16492         var progressRun = function() {
16493             if (!mods.length) {
16494                 Roo.debug && Roo.log('hide?');
16495                 if (!this.hideProgress && Roo.MessageBox) {
16496                     Roo.MessageBox.hide();
16497                 }
16498                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16499                 
16500                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16501                 
16502                 // THE END...
16503                 return false;   
16504             }
16505             
16506             var m = mods.shift();
16507             
16508             
16509             Roo.debug && Roo.log(m);
16510             // not sure if this is supported any more.. - modules that are are just function
16511             if (typeof(m) == 'function') { 
16512                 m.call(this);
16513                 return progressRun.defer(10, _this);
16514             } 
16515             
16516             
16517             msg = "Building Interface " + (total  - mods.length) + 
16518                     " of " + total + 
16519                     (m.name ? (' - ' + m.name) : '');
16520                         Roo.debug && Roo.log(msg);
16521             if (!this.hideProgress &&  Roo.MessageBox) { 
16522                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16523             }
16524             
16525          
16526             // is the module disabled?
16527             var disabled = (typeof(m.disabled) == 'function') ?
16528                 m.disabled.call(m.module.disabled) : m.disabled;    
16529             
16530             
16531             if (disabled) {
16532                 return progressRun(); // we do not update the display!
16533             }
16534             
16535             // now build 
16536             
16537                         
16538                         
16539             m.render();
16540             // it's 10 on top level, and 1 on others??? why...
16541             return progressRun.defer(10, _this);
16542              
16543         }
16544         progressRun.defer(1, _this);
16545      
16546         
16547         
16548     },
16549         
16550         
16551         /**
16552          * Event Object.
16553          *
16554          *
16555          */
16556         event: false, 
16557     /**
16558          * wrapper for event.on - aliased later..  
16559          * Typically use to register a event handler for register:
16560          *
16561          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16562          *
16563          */
16564     on : false
16565    
16566     
16567     
16568 });
16569
16570 Roo.XComponent.event = new Roo.util.Observable({
16571                 events : { 
16572                         /**
16573                          * @event register
16574                          * Fires when an Component is registered,
16575                          * set the disable property on the Component to stop registration.
16576                          * @param {Roo.XComponent} c the component being registerd.
16577                          * 
16578                          */
16579                         'register' : true,
16580             /**
16581                          * @event beforebuild
16582                          * Fires before each Component is built
16583                          * can be used to apply permissions.
16584                          * @param {Roo.XComponent} c the component being registerd.
16585                          * 
16586                          */
16587                         'beforebuild' : true,
16588                         /**
16589                          * @event buildcomplete
16590                          * Fires on the top level element when all elements have been built
16591                          * @param {Roo.XComponent} the top level component.
16592                          */
16593                         'buildcomplete' : true
16594                         
16595                 }
16596 });
16597
16598 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16599  //
16600  /**
16601  * marked - a markdown parser
16602  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16603  * https://github.com/chjj/marked
16604  */
16605
16606
16607 /**
16608  *
16609  * Roo.Markdown - is a very crude wrapper around marked..
16610  *
16611  * usage:
16612  * 
16613  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16614  * 
16615  * Note: move the sample code to the bottom of this
16616  * file before uncommenting it.
16617  *
16618  */
16619
16620 Roo.Markdown = {};
16621 Roo.Markdown.toHtml = function(text) {
16622     
16623     var c = new Roo.Markdown.marked.setOptions({
16624             renderer: new Roo.Markdown.marked.Renderer(),
16625             gfm: true,
16626             tables: true,
16627             breaks: false,
16628             pedantic: false,
16629             sanitize: false,
16630             smartLists: true,
16631             smartypants: false
16632           });
16633     // A FEW HACKS!!?
16634     
16635     text = text.replace(/\\\n/g,' ');
16636     return Roo.Markdown.marked(text);
16637 };
16638 //
16639 // converter
16640 //
16641 // Wraps all "globals" so that the only thing
16642 // exposed is makeHtml().
16643 //
16644 (function() {
16645     
16646     /**
16647      * Block-Level Grammar
16648      */
16649     
16650     var block = {
16651       newline: /^\n+/,
16652       code: /^( {4}[^\n]+\n*)+/,
16653       fences: noop,
16654       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16655       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16656       nptable: noop,
16657       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16658       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16659       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16660       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16661       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16662       table: noop,
16663       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16664       text: /^[^\n]+/
16665     };
16666     
16667     block.bullet = /(?:[*+-]|\d+\.)/;
16668     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16669     block.item = replace(block.item, 'gm')
16670       (/bull/g, block.bullet)
16671       ();
16672     
16673     block.list = replace(block.list)
16674       (/bull/g, block.bullet)
16675       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16676       ('def', '\\n+(?=' + block.def.source + ')')
16677       ();
16678     
16679     block.blockquote = replace(block.blockquote)
16680       ('def', block.def)
16681       ();
16682     
16683     block._tag = '(?!(?:'
16684       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16685       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16686       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16687     
16688     block.html = replace(block.html)
16689       ('comment', /<!--[\s\S]*?-->/)
16690       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16691       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16692       (/tag/g, block._tag)
16693       ();
16694     
16695     block.paragraph = replace(block.paragraph)
16696       ('hr', block.hr)
16697       ('heading', block.heading)
16698       ('lheading', block.lheading)
16699       ('blockquote', block.blockquote)
16700       ('tag', '<' + block._tag)
16701       ('def', block.def)
16702       ();
16703     
16704     /**
16705      * Normal Block Grammar
16706      */
16707     
16708     block.normal = merge({}, block);
16709     
16710     /**
16711      * GFM Block Grammar
16712      */
16713     
16714     block.gfm = merge({}, block.normal, {
16715       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16716       paragraph: /^/,
16717       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16718     });
16719     
16720     block.gfm.paragraph = replace(block.paragraph)
16721       ('(?!', '(?!'
16722         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16723         + block.list.source.replace('\\1', '\\3') + '|')
16724       ();
16725     
16726     /**
16727      * GFM + Tables Block Grammar
16728      */
16729     
16730     block.tables = merge({}, block.gfm, {
16731       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16732       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16733     });
16734     
16735     /**
16736      * Block Lexer
16737      */
16738     
16739     function Lexer(options) {
16740       this.tokens = [];
16741       this.tokens.links = {};
16742       this.options = options || marked.defaults;
16743       this.rules = block.normal;
16744     
16745       if (this.options.gfm) {
16746         if (this.options.tables) {
16747           this.rules = block.tables;
16748         } else {
16749           this.rules = block.gfm;
16750         }
16751       }
16752     }
16753     
16754     /**
16755      * Expose Block Rules
16756      */
16757     
16758     Lexer.rules = block;
16759     
16760     /**
16761      * Static Lex Method
16762      */
16763     
16764     Lexer.lex = function(src, options) {
16765       var lexer = new Lexer(options);
16766       return lexer.lex(src);
16767     };
16768     
16769     /**
16770      * Preprocessing
16771      */
16772     
16773     Lexer.prototype.lex = function(src) {
16774       src = src
16775         .replace(/\r\n|\r/g, '\n')
16776         .replace(/\t/g, '    ')
16777         .replace(/\u00a0/g, ' ')
16778         .replace(/\u2424/g, '\n');
16779     
16780       return this.token(src, true);
16781     };
16782     
16783     /**
16784      * Lexing
16785      */
16786     
16787     Lexer.prototype.token = function(src, top, bq) {
16788       var src = src.replace(/^ +$/gm, '')
16789         , next
16790         , loose
16791         , cap
16792         , bull
16793         , b
16794         , item
16795         , space
16796         , i
16797         , l;
16798     
16799       while (src) {
16800         // newline
16801         if (cap = this.rules.newline.exec(src)) {
16802           src = src.substring(cap[0].length);
16803           if (cap[0].length > 1) {
16804             this.tokens.push({
16805               type: 'space'
16806             });
16807           }
16808         }
16809     
16810         // code
16811         if (cap = this.rules.code.exec(src)) {
16812           src = src.substring(cap[0].length);
16813           cap = cap[0].replace(/^ {4}/gm, '');
16814           this.tokens.push({
16815             type: 'code',
16816             text: !this.options.pedantic
16817               ? cap.replace(/\n+$/, '')
16818               : cap
16819           });
16820           continue;
16821         }
16822     
16823         // fences (gfm)
16824         if (cap = this.rules.fences.exec(src)) {
16825           src = src.substring(cap[0].length);
16826           this.tokens.push({
16827             type: 'code',
16828             lang: cap[2],
16829             text: cap[3] || ''
16830           });
16831           continue;
16832         }
16833     
16834         // heading
16835         if (cap = this.rules.heading.exec(src)) {
16836           src = src.substring(cap[0].length);
16837           this.tokens.push({
16838             type: 'heading',
16839             depth: cap[1].length,
16840             text: cap[2]
16841           });
16842           continue;
16843         }
16844     
16845         // table no leading pipe (gfm)
16846         if (top && (cap = this.rules.nptable.exec(src))) {
16847           src = src.substring(cap[0].length);
16848     
16849           item = {
16850             type: 'table',
16851             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16852             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16853             cells: cap[3].replace(/\n$/, '').split('\n')
16854           };
16855     
16856           for (i = 0; i < item.align.length; i++) {
16857             if (/^ *-+: *$/.test(item.align[i])) {
16858               item.align[i] = 'right';
16859             } else if (/^ *:-+: *$/.test(item.align[i])) {
16860               item.align[i] = 'center';
16861             } else if (/^ *:-+ *$/.test(item.align[i])) {
16862               item.align[i] = 'left';
16863             } else {
16864               item.align[i] = null;
16865             }
16866           }
16867     
16868           for (i = 0; i < item.cells.length; i++) {
16869             item.cells[i] = item.cells[i].split(/ *\| */);
16870           }
16871     
16872           this.tokens.push(item);
16873     
16874           continue;
16875         }
16876     
16877         // lheading
16878         if (cap = this.rules.lheading.exec(src)) {
16879           src = src.substring(cap[0].length);
16880           this.tokens.push({
16881             type: 'heading',
16882             depth: cap[2] === '=' ? 1 : 2,
16883             text: cap[1]
16884           });
16885           continue;
16886         }
16887     
16888         // hr
16889         if (cap = this.rules.hr.exec(src)) {
16890           src = src.substring(cap[0].length);
16891           this.tokens.push({
16892             type: 'hr'
16893           });
16894           continue;
16895         }
16896     
16897         // blockquote
16898         if (cap = this.rules.blockquote.exec(src)) {
16899           src = src.substring(cap[0].length);
16900     
16901           this.tokens.push({
16902             type: 'blockquote_start'
16903           });
16904     
16905           cap = cap[0].replace(/^ *> ?/gm, '');
16906     
16907           // Pass `top` to keep the current
16908           // "toplevel" state. This is exactly
16909           // how markdown.pl works.
16910           this.token(cap, top, true);
16911     
16912           this.tokens.push({
16913             type: 'blockquote_end'
16914           });
16915     
16916           continue;
16917         }
16918     
16919         // list
16920         if (cap = this.rules.list.exec(src)) {
16921           src = src.substring(cap[0].length);
16922           bull = cap[2];
16923     
16924           this.tokens.push({
16925             type: 'list_start',
16926             ordered: bull.length > 1
16927           });
16928     
16929           // Get each top-level item.
16930           cap = cap[0].match(this.rules.item);
16931     
16932           next = false;
16933           l = cap.length;
16934           i = 0;
16935     
16936           for (; i < l; i++) {
16937             item = cap[i];
16938     
16939             // Remove the list item's bullet
16940             // so it is seen as the next token.
16941             space = item.length;
16942             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16943     
16944             // Outdent whatever the
16945             // list item contains. Hacky.
16946             if (~item.indexOf('\n ')) {
16947               space -= item.length;
16948               item = !this.options.pedantic
16949                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16950                 : item.replace(/^ {1,4}/gm, '');
16951             }
16952     
16953             // Determine whether the next list item belongs here.
16954             // Backpedal if it does not belong in this list.
16955             if (this.options.smartLists && i !== l - 1) {
16956               b = block.bullet.exec(cap[i + 1])[0];
16957               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
16958                 src = cap.slice(i + 1).join('\n') + src;
16959                 i = l - 1;
16960               }
16961             }
16962     
16963             // Determine whether item is loose or not.
16964             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
16965             // for discount behavior.
16966             loose = next || /\n\n(?!\s*$)/.test(item);
16967             if (i !== l - 1) {
16968               next = item.charAt(item.length - 1) === '\n';
16969               if (!loose) { loose = next; }
16970             }
16971     
16972             this.tokens.push({
16973               type: loose
16974                 ? 'loose_item_start'
16975                 : 'list_item_start'
16976             });
16977     
16978             // Recurse.
16979             this.token(item, false, bq);
16980     
16981             this.tokens.push({
16982               type: 'list_item_end'
16983             });
16984           }
16985     
16986           this.tokens.push({
16987             type: 'list_end'
16988           });
16989     
16990           continue;
16991         }
16992     
16993         // html
16994         if (cap = this.rules.html.exec(src)) {
16995           src = src.substring(cap[0].length);
16996           this.tokens.push({
16997             type: this.options.sanitize
16998               ? 'paragraph'
16999               : 'html',
17000             pre: !this.options.sanitizer
17001               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17002             text: cap[0]
17003           });
17004           continue;
17005         }
17006     
17007         // def
17008         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17009           src = src.substring(cap[0].length);
17010           this.tokens.links[cap[1].toLowerCase()] = {
17011             href: cap[2],
17012             title: cap[3]
17013           };
17014           continue;
17015         }
17016     
17017         // table (gfm)
17018         if (top && (cap = this.rules.table.exec(src))) {
17019           src = src.substring(cap[0].length);
17020     
17021           item = {
17022             type: 'table',
17023             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17024             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17025             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17026           };
17027     
17028           for (i = 0; i < item.align.length; i++) {
17029             if (/^ *-+: *$/.test(item.align[i])) {
17030               item.align[i] = 'right';
17031             } else if (/^ *:-+: *$/.test(item.align[i])) {
17032               item.align[i] = 'center';
17033             } else if (/^ *:-+ *$/.test(item.align[i])) {
17034               item.align[i] = 'left';
17035             } else {
17036               item.align[i] = null;
17037             }
17038           }
17039     
17040           for (i = 0; i < item.cells.length; i++) {
17041             item.cells[i] = item.cells[i]
17042               .replace(/^ *\| *| *\| *$/g, '')
17043               .split(/ *\| */);
17044           }
17045     
17046           this.tokens.push(item);
17047     
17048           continue;
17049         }
17050     
17051         // top-level paragraph
17052         if (top && (cap = this.rules.paragraph.exec(src))) {
17053           src = src.substring(cap[0].length);
17054           this.tokens.push({
17055             type: 'paragraph',
17056             text: cap[1].charAt(cap[1].length - 1) === '\n'
17057               ? cap[1].slice(0, -1)
17058               : cap[1]
17059           });
17060           continue;
17061         }
17062     
17063         // text
17064         if (cap = this.rules.text.exec(src)) {
17065           // Top-level should never reach here.
17066           src = src.substring(cap[0].length);
17067           this.tokens.push({
17068             type: 'text',
17069             text: cap[0]
17070           });
17071           continue;
17072         }
17073     
17074         if (src) {
17075           throw new
17076             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17077         }
17078       }
17079     
17080       return this.tokens;
17081     };
17082     
17083     /**
17084      * Inline-Level Grammar
17085      */
17086     
17087     var inline = {
17088       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17089       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17090       url: noop,
17091       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17092       link: /^!?\[(inside)\]\(href\)/,
17093       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17094       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17095       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17096       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17097       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17098       br: /^ {2,}\n(?!\s*$)/,
17099       del: noop,
17100       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17101     };
17102     
17103     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17104     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17105     
17106     inline.link = replace(inline.link)
17107       ('inside', inline._inside)
17108       ('href', inline._href)
17109       ();
17110     
17111     inline.reflink = replace(inline.reflink)
17112       ('inside', inline._inside)
17113       ();
17114     
17115     /**
17116      * Normal Inline Grammar
17117      */
17118     
17119     inline.normal = merge({}, inline);
17120     
17121     /**
17122      * Pedantic Inline Grammar
17123      */
17124     
17125     inline.pedantic = merge({}, inline.normal, {
17126       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17127       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17128     });
17129     
17130     /**
17131      * GFM Inline Grammar
17132      */
17133     
17134     inline.gfm = merge({}, inline.normal, {
17135       escape: replace(inline.escape)('])', '~|])')(),
17136       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17137       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17138       text: replace(inline.text)
17139         (']|', '~]|')
17140         ('|', '|https?://|')
17141         ()
17142     });
17143     
17144     /**
17145      * GFM + Line Breaks Inline Grammar
17146      */
17147     
17148     inline.breaks = merge({}, inline.gfm, {
17149       br: replace(inline.br)('{2,}', '*')(),
17150       text: replace(inline.gfm.text)('{2,}', '*')()
17151     });
17152     
17153     /**
17154      * Inline Lexer & Compiler
17155      */
17156     
17157     function InlineLexer(links, options) {
17158       this.options = options || marked.defaults;
17159       this.links = links;
17160       this.rules = inline.normal;
17161       this.renderer = this.options.renderer || new Renderer;
17162       this.renderer.options = this.options;
17163     
17164       if (!this.links) {
17165         throw new
17166           Error('Tokens array requires a `links` property.');
17167       }
17168     
17169       if (this.options.gfm) {
17170         if (this.options.breaks) {
17171           this.rules = inline.breaks;
17172         } else {
17173           this.rules = inline.gfm;
17174         }
17175       } else if (this.options.pedantic) {
17176         this.rules = inline.pedantic;
17177       }
17178     }
17179     
17180     /**
17181      * Expose Inline Rules
17182      */
17183     
17184     InlineLexer.rules = inline;
17185     
17186     /**
17187      * Static Lexing/Compiling Method
17188      */
17189     
17190     InlineLexer.output = function(src, links, options) {
17191       var inline = new InlineLexer(links, options);
17192       return inline.output(src);
17193     };
17194     
17195     /**
17196      * Lexing/Compiling
17197      */
17198     
17199     InlineLexer.prototype.output = function(src) {
17200       var out = ''
17201         , link
17202         , text
17203         , href
17204         , cap;
17205     
17206       while (src) {
17207         // escape
17208         if (cap = this.rules.escape.exec(src)) {
17209           src = src.substring(cap[0].length);
17210           out += cap[1];
17211           continue;
17212         }
17213     
17214         // autolink
17215         if (cap = this.rules.autolink.exec(src)) {
17216           src = src.substring(cap[0].length);
17217           if (cap[2] === '@') {
17218             text = cap[1].charAt(6) === ':'
17219               ? this.mangle(cap[1].substring(7))
17220               : this.mangle(cap[1]);
17221             href = this.mangle('mailto:') + text;
17222           } else {
17223             text = escape(cap[1]);
17224             href = text;
17225           }
17226           out += this.renderer.link(href, null, text);
17227           continue;
17228         }
17229     
17230         // url (gfm)
17231         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17232           src = src.substring(cap[0].length);
17233           text = escape(cap[1]);
17234           href = text;
17235           out += this.renderer.link(href, null, text);
17236           continue;
17237         }
17238     
17239         // tag
17240         if (cap = this.rules.tag.exec(src)) {
17241           if (!this.inLink && /^<a /i.test(cap[0])) {
17242             this.inLink = true;
17243           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17244             this.inLink = false;
17245           }
17246           src = src.substring(cap[0].length);
17247           out += this.options.sanitize
17248             ? this.options.sanitizer
17249               ? this.options.sanitizer(cap[0])
17250               : escape(cap[0])
17251             : cap[0];
17252           continue;
17253         }
17254     
17255         // link
17256         if (cap = this.rules.link.exec(src)) {
17257           src = src.substring(cap[0].length);
17258           this.inLink = true;
17259           out += this.outputLink(cap, {
17260             href: cap[2],
17261             title: cap[3]
17262           });
17263           this.inLink = false;
17264           continue;
17265         }
17266     
17267         // reflink, nolink
17268         if ((cap = this.rules.reflink.exec(src))
17269             || (cap = this.rules.nolink.exec(src))) {
17270           src = src.substring(cap[0].length);
17271           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17272           link = this.links[link.toLowerCase()];
17273           if (!link || !link.href) {
17274             out += cap[0].charAt(0);
17275             src = cap[0].substring(1) + src;
17276             continue;
17277           }
17278           this.inLink = true;
17279           out += this.outputLink(cap, link);
17280           this.inLink = false;
17281           continue;
17282         }
17283     
17284         // strong
17285         if (cap = this.rules.strong.exec(src)) {
17286           src = src.substring(cap[0].length);
17287           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17288           continue;
17289         }
17290     
17291         // em
17292         if (cap = this.rules.em.exec(src)) {
17293           src = src.substring(cap[0].length);
17294           out += this.renderer.em(this.output(cap[2] || cap[1]));
17295           continue;
17296         }
17297     
17298         // code
17299         if (cap = this.rules.code.exec(src)) {
17300           src = src.substring(cap[0].length);
17301           out += this.renderer.codespan(escape(cap[2], true));
17302           continue;
17303         }
17304     
17305         // br
17306         if (cap = this.rules.br.exec(src)) {
17307           src = src.substring(cap[0].length);
17308           out += this.renderer.br();
17309           continue;
17310         }
17311     
17312         // del (gfm)
17313         if (cap = this.rules.del.exec(src)) {
17314           src = src.substring(cap[0].length);
17315           out += this.renderer.del(this.output(cap[1]));
17316           continue;
17317         }
17318     
17319         // text
17320         if (cap = this.rules.text.exec(src)) {
17321           src = src.substring(cap[0].length);
17322           out += this.renderer.text(escape(this.smartypants(cap[0])));
17323           continue;
17324         }
17325     
17326         if (src) {
17327           throw new
17328             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17329         }
17330       }
17331     
17332       return out;
17333     };
17334     
17335     /**
17336      * Compile Link
17337      */
17338     
17339     InlineLexer.prototype.outputLink = function(cap, link) {
17340       var href = escape(link.href)
17341         , title = link.title ? escape(link.title) : null;
17342     
17343       return cap[0].charAt(0) !== '!'
17344         ? this.renderer.link(href, title, this.output(cap[1]))
17345         : this.renderer.image(href, title, escape(cap[1]));
17346     };
17347     
17348     /**
17349      * Smartypants Transformations
17350      */
17351     
17352     InlineLexer.prototype.smartypants = function(text) {
17353       if (!this.options.smartypants)  { return text; }
17354       return text
17355         // em-dashes
17356         .replace(/---/g, '\u2014')
17357         // en-dashes
17358         .replace(/--/g, '\u2013')
17359         // opening singles
17360         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17361         // closing singles & apostrophes
17362         .replace(/'/g, '\u2019')
17363         // opening doubles
17364         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17365         // closing doubles
17366         .replace(/"/g, '\u201d')
17367         // ellipses
17368         .replace(/\.{3}/g, '\u2026');
17369     };
17370     
17371     /**
17372      * Mangle Links
17373      */
17374     
17375     InlineLexer.prototype.mangle = function(text) {
17376       if (!this.options.mangle) { return text; }
17377       var out = ''
17378         , l = text.length
17379         , i = 0
17380         , ch;
17381     
17382       for (; i < l; i++) {
17383         ch = text.charCodeAt(i);
17384         if (Math.random() > 0.5) {
17385           ch = 'x' + ch.toString(16);
17386         }
17387         out += '&#' + ch + ';';
17388       }
17389     
17390       return out;
17391     };
17392     
17393     /**
17394      * Renderer
17395      */
17396     
17397     function Renderer(options) {
17398       this.options = options || {};
17399     }
17400     
17401     Renderer.prototype.code = function(code, lang, escaped) {
17402       if (this.options.highlight) {
17403         var out = this.options.highlight(code, lang);
17404         if (out != null && out !== code) {
17405           escaped = true;
17406           code = out;
17407         }
17408       } else {
17409             // hack!!! - it's already escapeD?
17410             escaped = true;
17411       }
17412     
17413       if (!lang) {
17414         return '<pre><code>'
17415           + (escaped ? code : escape(code, true))
17416           + '\n</code></pre>';
17417       }
17418     
17419       return '<pre><code class="'
17420         + this.options.langPrefix
17421         + escape(lang, true)
17422         + '">'
17423         + (escaped ? code : escape(code, true))
17424         + '\n</code></pre>\n';
17425     };
17426     
17427     Renderer.prototype.blockquote = function(quote) {
17428       return '<blockquote>\n' + quote + '</blockquote>\n';
17429     };
17430     
17431     Renderer.prototype.html = function(html) {
17432       return html;
17433     };
17434     
17435     Renderer.prototype.heading = function(text, level, raw) {
17436       return '<h'
17437         + level
17438         + ' id="'
17439         + this.options.headerPrefix
17440         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17441         + '">'
17442         + text
17443         + '</h'
17444         + level
17445         + '>\n';
17446     };
17447     
17448     Renderer.prototype.hr = function() {
17449       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17450     };
17451     
17452     Renderer.prototype.list = function(body, ordered) {
17453       var type = ordered ? 'ol' : 'ul';
17454       return '<' + type + '>\n' + body + '</' + type + '>\n';
17455     };
17456     
17457     Renderer.prototype.listitem = function(text) {
17458       return '<li>' + text + '</li>\n';
17459     };
17460     
17461     Renderer.prototype.paragraph = function(text) {
17462       return '<p>' + text + '</p>\n';
17463     };
17464     
17465     Renderer.prototype.table = function(header, body) {
17466       return '<table class="table table-striped">\n'
17467         + '<thead>\n'
17468         + header
17469         + '</thead>\n'
17470         + '<tbody>\n'
17471         + body
17472         + '</tbody>\n'
17473         + '</table>\n';
17474     };
17475     
17476     Renderer.prototype.tablerow = function(content) {
17477       return '<tr>\n' + content + '</tr>\n';
17478     };
17479     
17480     Renderer.prototype.tablecell = function(content, flags) {
17481       var type = flags.header ? 'th' : 'td';
17482       var tag = flags.align
17483         ? '<' + type + ' style="text-align:' + flags.align + '">'
17484         : '<' + type + '>';
17485       return tag + content + '</' + type + '>\n';
17486     };
17487     
17488     // span level renderer
17489     Renderer.prototype.strong = function(text) {
17490       return '<strong>' + text + '</strong>';
17491     };
17492     
17493     Renderer.prototype.em = function(text) {
17494       return '<em>' + text + '</em>';
17495     };
17496     
17497     Renderer.prototype.codespan = function(text) {
17498       return '<code>' + text + '</code>';
17499     };
17500     
17501     Renderer.prototype.br = function() {
17502       return this.options.xhtml ? '<br/>' : '<br>';
17503     };
17504     
17505     Renderer.prototype.del = function(text) {
17506       return '<del>' + text + '</del>';
17507     };
17508     
17509     Renderer.prototype.link = function(href, title, text) {
17510       if (this.options.sanitize) {
17511         try {
17512           var prot = decodeURIComponent(unescape(href))
17513             .replace(/[^\w:]/g, '')
17514             .toLowerCase();
17515         } catch (e) {
17516           return '';
17517         }
17518         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17519           return '';
17520         }
17521       }
17522       var out = '<a href="' + href + '"';
17523       if (title) {
17524         out += ' title="' + title + '"';
17525       }
17526       out += '>' + text + '</a>';
17527       return out;
17528     };
17529     
17530     Renderer.prototype.image = function(href, title, text) {
17531       var out = '<img src="' + href + '" alt="' + text + '"';
17532       if (title) {
17533         out += ' title="' + title + '"';
17534       }
17535       out += this.options.xhtml ? '/>' : '>';
17536       return out;
17537     };
17538     
17539     Renderer.prototype.text = function(text) {
17540       return text;
17541     };
17542     
17543     /**
17544      * Parsing & Compiling
17545      */
17546     
17547     function Parser(options) {
17548       this.tokens = [];
17549       this.token = null;
17550       this.options = options || marked.defaults;
17551       this.options.renderer = this.options.renderer || new Renderer;
17552       this.renderer = this.options.renderer;
17553       this.renderer.options = this.options;
17554     }
17555     
17556     /**
17557      * Static Parse Method
17558      */
17559     
17560     Parser.parse = function(src, options, renderer) {
17561       var parser = new Parser(options, renderer);
17562       return parser.parse(src);
17563     };
17564     
17565     /**
17566      * Parse Loop
17567      */
17568     
17569     Parser.prototype.parse = function(src) {
17570       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17571       this.tokens = src.reverse();
17572     
17573       var out = '';
17574       while (this.next()) {
17575         out += this.tok();
17576       }
17577     
17578       return out;
17579     };
17580     
17581     /**
17582      * Next Token
17583      */
17584     
17585     Parser.prototype.next = function() {
17586       return this.token = this.tokens.pop();
17587     };
17588     
17589     /**
17590      * Preview Next Token
17591      */
17592     
17593     Parser.prototype.peek = function() {
17594       return this.tokens[this.tokens.length - 1] || 0;
17595     };
17596     
17597     /**
17598      * Parse Text Tokens
17599      */
17600     
17601     Parser.prototype.parseText = function() {
17602       var body = this.token.text;
17603     
17604       while (this.peek().type === 'text') {
17605         body += '\n' + this.next().text;
17606       }
17607     
17608       return this.inline.output(body);
17609     };
17610     
17611     /**
17612      * Parse Current Token
17613      */
17614     
17615     Parser.prototype.tok = function() {
17616       switch (this.token.type) {
17617         case 'space': {
17618           return '';
17619         }
17620         case 'hr': {
17621           return this.renderer.hr();
17622         }
17623         case 'heading': {
17624           return this.renderer.heading(
17625             this.inline.output(this.token.text),
17626             this.token.depth,
17627             this.token.text);
17628         }
17629         case 'code': {
17630           return this.renderer.code(this.token.text,
17631             this.token.lang,
17632             this.token.escaped);
17633         }
17634         case 'table': {
17635           var header = ''
17636             , body = ''
17637             , i
17638             , row
17639             , cell
17640             , flags
17641             , j;
17642     
17643           // header
17644           cell = '';
17645           for (i = 0; i < this.token.header.length; i++) {
17646             flags = { header: true, align: this.token.align[i] };
17647             cell += this.renderer.tablecell(
17648               this.inline.output(this.token.header[i]),
17649               { header: true, align: this.token.align[i] }
17650             );
17651           }
17652           header += this.renderer.tablerow(cell);
17653     
17654           for (i = 0; i < this.token.cells.length; i++) {
17655             row = this.token.cells[i];
17656     
17657             cell = '';
17658             for (j = 0; j < row.length; j++) {
17659               cell += this.renderer.tablecell(
17660                 this.inline.output(row[j]),
17661                 { header: false, align: this.token.align[j] }
17662               );
17663             }
17664     
17665             body += this.renderer.tablerow(cell);
17666           }
17667           return this.renderer.table(header, body);
17668         }
17669         case 'blockquote_start': {
17670           var body = '';
17671     
17672           while (this.next().type !== 'blockquote_end') {
17673             body += this.tok();
17674           }
17675     
17676           return this.renderer.blockquote(body);
17677         }
17678         case 'list_start': {
17679           var body = ''
17680             , ordered = this.token.ordered;
17681     
17682           while (this.next().type !== 'list_end') {
17683             body += this.tok();
17684           }
17685     
17686           return this.renderer.list(body, ordered);
17687         }
17688         case 'list_item_start': {
17689           var body = '';
17690     
17691           while (this.next().type !== 'list_item_end') {
17692             body += this.token.type === 'text'
17693               ? this.parseText()
17694               : this.tok();
17695           }
17696     
17697           return this.renderer.listitem(body);
17698         }
17699         case 'loose_item_start': {
17700           var body = '';
17701     
17702           while (this.next().type !== 'list_item_end') {
17703             body += this.tok();
17704           }
17705     
17706           return this.renderer.listitem(body);
17707         }
17708         case 'html': {
17709           var html = !this.token.pre && !this.options.pedantic
17710             ? this.inline.output(this.token.text)
17711             : this.token.text;
17712           return this.renderer.html(html);
17713         }
17714         case 'paragraph': {
17715           return this.renderer.paragraph(this.inline.output(this.token.text));
17716         }
17717         case 'text': {
17718           return this.renderer.paragraph(this.parseText());
17719         }
17720       }
17721     };
17722     
17723     /**
17724      * Helpers
17725      */
17726     
17727     function escape(html, encode) {
17728       return html
17729         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17730         .replace(/</g, '&lt;')
17731         .replace(/>/g, '&gt;')
17732         .replace(/"/g, '&quot;')
17733         .replace(/'/g, '&#39;');
17734     }
17735     
17736     function unescape(html) {
17737         // explicitly match decimal, hex, and named HTML entities 
17738       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17739         n = n.toLowerCase();
17740         if (n === 'colon') { return ':'; }
17741         if (n.charAt(0) === '#') {
17742           return n.charAt(1) === 'x'
17743             ? String.fromCharCode(parseInt(n.substring(2), 16))
17744             : String.fromCharCode(+n.substring(1));
17745         }
17746         return '';
17747       });
17748     }
17749     
17750     function replace(regex, opt) {
17751       regex = regex.source;
17752       opt = opt || '';
17753       return function self(name, val) {
17754         if (!name) { return new RegExp(regex, opt); }
17755         val = val.source || val;
17756         val = val.replace(/(^|[^\[])\^/g, '$1');
17757         regex = regex.replace(name, val);
17758         return self;
17759       };
17760     }
17761     
17762     function noop() {}
17763     noop.exec = noop;
17764     
17765     function merge(obj) {
17766       var i = 1
17767         , target
17768         , key;
17769     
17770       for (; i < arguments.length; i++) {
17771         target = arguments[i];
17772         for (key in target) {
17773           if (Object.prototype.hasOwnProperty.call(target, key)) {
17774             obj[key] = target[key];
17775           }
17776         }
17777       }
17778     
17779       return obj;
17780     }
17781     
17782     
17783     /**
17784      * Marked
17785      */
17786     
17787     function marked(src, opt, callback) {
17788       if (callback || typeof opt === 'function') {
17789         if (!callback) {
17790           callback = opt;
17791           opt = null;
17792         }
17793     
17794         opt = merge({}, marked.defaults, opt || {});
17795     
17796         var highlight = opt.highlight
17797           , tokens
17798           , pending
17799           , i = 0;
17800     
17801         try {
17802           tokens = Lexer.lex(src, opt)
17803         } catch (e) {
17804           return callback(e);
17805         }
17806     
17807         pending = tokens.length;
17808     
17809         var done = function(err) {
17810           if (err) {
17811             opt.highlight = highlight;
17812             return callback(err);
17813           }
17814     
17815           var out;
17816     
17817           try {
17818             out = Parser.parse(tokens, opt);
17819           } catch (e) {
17820             err = e;
17821           }
17822     
17823           opt.highlight = highlight;
17824     
17825           return err
17826             ? callback(err)
17827             : callback(null, out);
17828         };
17829     
17830         if (!highlight || highlight.length < 3) {
17831           return done();
17832         }
17833     
17834         delete opt.highlight;
17835     
17836         if (!pending) { return done(); }
17837     
17838         for (; i < tokens.length; i++) {
17839           (function(token) {
17840             if (token.type !== 'code') {
17841               return --pending || done();
17842             }
17843             return highlight(token.text, token.lang, function(err, code) {
17844               if (err) { return done(err); }
17845               if (code == null || code === token.text) {
17846                 return --pending || done();
17847               }
17848               token.text = code;
17849               token.escaped = true;
17850               --pending || done();
17851             });
17852           })(tokens[i]);
17853         }
17854     
17855         return;
17856       }
17857       try {
17858         if (opt) { opt = merge({}, marked.defaults, opt); }
17859         return Parser.parse(Lexer.lex(src, opt), opt);
17860       } catch (e) {
17861         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17862         if ((opt || marked.defaults).silent) {
17863           return '<p>An error occured:</p><pre>'
17864             + escape(e.message + '', true)
17865             + '</pre>';
17866         }
17867         throw e;
17868       }
17869     }
17870     
17871     /**
17872      * Options
17873      */
17874     
17875     marked.options =
17876     marked.setOptions = function(opt) {
17877       merge(marked.defaults, opt);
17878       return marked;
17879     };
17880     
17881     marked.defaults = {
17882       gfm: true,
17883       tables: true,
17884       breaks: false,
17885       pedantic: false,
17886       sanitize: false,
17887       sanitizer: null,
17888       mangle: true,
17889       smartLists: false,
17890       silent: false,
17891       highlight: null,
17892       langPrefix: 'lang-',
17893       smartypants: false,
17894       headerPrefix: '',
17895       renderer: new Renderer,
17896       xhtml: false
17897     };
17898     
17899     /**
17900      * Expose
17901      */
17902     
17903     marked.Parser = Parser;
17904     marked.parser = Parser.parse;
17905     
17906     marked.Renderer = Renderer;
17907     
17908     marked.Lexer = Lexer;
17909     marked.lexer = Lexer.lex;
17910     
17911     marked.InlineLexer = InlineLexer;
17912     marked.inlineLexer = InlineLexer.output;
17913     
17914     marked.parse = marked;
17915     
17916     Roo.Markdown.marked = marked;
17917
17918 })();/*
17919  * Based on:
17920  * Ext JS Library 1.1.1
17921  * Copyright(c) 2006-2007, Ext JS, LLC.
17922  *
17923  * Originally Released Under LGPL - original licence link has changed is not relivant.
17924  *
17925  * Fork - LGPL
17926  * <script type="text/javascript">
17927  */
17928
17929
17930
17931 /*
17932  * These classes are derivatives of the similarly named classes in the YUI Library.
17933  * The original license:
17934  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17935  * Code licensed under the BSD License:
17936  * http://developer.yahoo.net/yui/license.txt
17937  */
17938
17939 (function() {
17940
17941 var Event=Roo.EventManager;
17942 var Dom=Roo.lib.Dom;
17943
17944 /**
17945  * @class Roo.dd.DragDrop
17946  * @extends Roo.util.Observable
17947  * Defines the interface and base operation of items that that can be
17948  * dragged or can be drop targets.  It was designed to be extended, overriding
17949  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17950  * Up to three html elements can be associated with a DragDrop instance:
17951  * <ul>
17952  * <li>linked element: the element that is passed into the constructor.
17953  * This is the element which defines the boundaries for interaction with
17954  * other DragDrop objects.</li>
17955  * <li>handle element(s): The drag operation only occurs if the element that
17956  * was clicked matches a handle element.  By default this is the linked
17957  * element, but there are times that you will want only a portion of the
17958  * linked element to initiate the drag operation, and the setHandleElId()
17959  * method provides a way to define this.</li>
17960  * <li>drag element: this represents the element that would be moved along
17961  * with the cursor during a drag operation.  By default, this is the linked
17962  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
17963  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
17964  * </li>
17965  * </ul>
17966  * This class should not be instantiated until the onload event to ensure that
17967  * the associated elements are available.
17968  * The following would define a DragDrop obj that would interact with any
17969  * other DragDrop obj in the "group1" group:
17970  * <pre>
17971  *  dd = new Roo.dd.DragDrop("div1", "group1");
17972  * </pre>
17973  * Since none of the event handlers have been implemented, nothing would
17974  * actually happen if you were to run the code above.  Normally you would
17975  * override this class or one of the default implementations, but you can
17976  * also override the methods you want on an instance of the class...
17977  * <pre>
17978  *  dd.onDragDrop = function(e, id) {
17979  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
17980  *  }
17981  * </pre>
17982  * @constructor
17983  * @param {String} id of the element that is linked to this instance
17984  * @param {String} sGroup the group of related DragDrop objects
17985  * @param {object} config an object containing configurable attributes
17986  *                Valid properties for DragDrop:
17987  *                    padding, isTarget, maintainOffset, primaryButtonOnly
17988  */
17989 Roo.dd.DragDrop = function(id, sGroup, config) {
17990     if (id) {
17991         this.init(id, sGroup, config);
17992     }
17993     
17994 };
17995
17996 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
17997
17998     /**
17999      * The id of the element associated with this object.  This is what we
18000      * refer to as the "linked element" because the size and position of
18001      * this element is used to determine when the drag and drop objects have
18002      * interacted.
18003      * @property id
18004      * @type String
18005      */
18006     id: null,
18007
18008     /**
18009      * Configuration attributes passed into the constructor
18010      * @property config
18011      * @type object
18012      */
18013     config: null,
18014
18015     /**
18016      * The id of the element that will be dragged.  By default this is same
18017      * as the linked element , but could be changed to another element. Ex:
18018      * Roo.dd.DDProxy
18019      * @property dragElId
18020      * @type String
18021      * @private
18022      */
18023     dragElId: null,
18024
18025     /**
18026      * the id of the element that initiates the drag operation.  By default
18027      * this is the linked element, but could be changed to be a child of this
18028      * element.  This lets us do things like only starting the drag when the
18029      * header element within the linked html element is clicked.
18030      * @property handleElId
18031      * @type String
18032      * @private
18033      */
18034     handleElId: null,
18035
18036     /**
18037      * An associative array of HTML tags that will be ignored if clicked.
18038      * @property invalidHandleTypes
18039      * @type {string: string}
18040      */
18041     invalidHandleTypes: null,
18042
18043     /**
18044      * An associative array of ids for elements that will be ignored if clicked
18045      * @property invalidHandleIds
18046      * @type {string: string}
18047      */
18048     invalidHandleIds: null,
18049
18050     /**
18051      * An indexted array of css class names for elements that will be ignored
18052      * if clicked.
18053      * @property invalidHandleClasses
18054      * @type string[]
18055      */
18056     invalidHandleClasses: null,
18057
18058     /**
18059      * The linked element's absolute X position at the time the drag was
18060      * started
18061      * @property startPageX
18062      * @type int
18063      * @private
18064      */
18065     startPageX: 0,
18066
18067     /**
18068      * The linked element's absolute X position at the time the drag was
18069      * started
18070      * @property startPageY
18071      * @type int
18072      * @private
18073      */
18074     startPageY: 0,
18075
18076     /**
18077      * The group defines a logical collection of DragDrop objects that are
18078      * related.  Instances only get events when interacting with other
18079      * DragDrop object in the same group.  This lets us define multiple
18080      * groups using a single DragDrop subclass if we want.
18081      * @property groups
18082      * @type {string: string}
18083      */
18084     groups: null,
18085
18086     /**
18087      * Individual drag/drop instances can be locked.  This will prevent
18088      * onmousedown start drag.
18089      * @property locked
18090      * @type boolean
18091      * @private
18092      */
18093     locked: false,
18094
18095     /**
18096      * Lock this instance
18097      * @method lock
18098      */
18099     lock: function() { this.locked = true; },
18100
18101     /**
18102      * Unlock this instace
18103      * @method unlock
18104      */
18105     unlock: function() { this.locked = false; },
18106
18107     /**
18108      * By default, all insances can be a drop target.  This can be disabled by
18109      * setting isTarget to false.
18110      * @method isTarget
18111      * @type boolean
18112      */
18113     isTarget: true,
18114
18115     /**
18116      * The padding configured for this drag and drop object for calculating
18117      * the drop zone intersection with this object.
18118      * @method padding
18119      * @type int[]
18120      */
18121     padding: null,
18122
18123     /**
18124      * Cached reference to the linked element
18125      * @property _domRef
18126      * @private
18127      */
18128     _domRef: null,
18129
18130     /**
18131      * Internal typeof flag
18132      * @property __ygDragDrop
18133      * @private
18134      */
18135     __ygDragDrop: true,
18136
18137     /**
18138      * Set to true when horizontal contraints are applied
18139      * @property constrainX
18140      * @type boolean
18141      * @private
18142      */
18143     constrainX: false,
18144
18145     /**
18146      * Set to true when vertical contraints are applied
18147      * @property constrainY
18148      * @type boolean
18149      * @private
18150      */
18151     constrainY: false,
18152
18153     /**
18154      * The left constraint
18155      * @property minX
18156      * @type int
18157      * @private
18158      */
18159     minX: 0,
18160
18161     /**
18162      * The right constraint
18163      * @property maxX
18164      * @type int
18165      * @private
18166      */
18167     maxX: 0,
18168
18169     /**
18170      * The up constraint
18171      * @property minY
18172      * @type int
18173      * @type int
18174      * @private
18175      */
18176     minY: 0,
18177
18178     /**
18179      * The down constraint
18180      * @property maxY
18181      * @type int
18182      * @private
18183      */
18184     maxY: 0,
18185
18186     /**
18187      * Maintain offsets when we resetconstraints.  Set to true when you want
18188      * the position of the element relative to its parent to stay the same
18189      * when the page changes
18190      *
18191      * @property maintainOffset
18192      * @type boolean
18193      */
18194     maintainOffset: false,
18195
18196     /**
18197      * Array of pixel locations the element will snap to if we specified a
18198      * horizontal graduation/interval.  This array is generated automatically
18199      * when you define a tick interval.
18200      * @property xTicks
18201      * @type int[]
18202      */
18203     xTicks: null,
18204
18205     /**
18206      * Array of pixel locations the element will snap to if we specified a
18207      * vertical graduation/interval.  This array is generated automatically
18208      * when you define a tick interval.
18209      * @property yTicks
18210      * @type int[]
18211      */
18212     yTicks: null,
18213
18214     /**
18215      * By default the drag and drop instance will only respond to the primary
18216      * button click (left button for a right-handed mouse).  Set to true to
18217      * allow drag and drop to start with any mouse click that is propogated
18218      * by the browser
18219      * @property primaryButtonOnly
18220      * @type boolean
18221      */
18222     primaryButtonOnly: true,
18223
18224     /**
18225      * The availabe property is false until the linked dom element is accessible.
18226      * @property available
18227      * @type boolean
18228      */
18229     available: false,
18230
18231     /**
18232      * By default, drags can only be initiated if the mousedown occurs in the
18233      * region the linked element is.  This is done in part to work around a
18234      * bug in some browsers that mis-report the mousedown if the previous
18235      * mouseup happened outside of the window.  This property is set to true
18236      * if outer handles are defined.
18237      *
18238      * @property hasOuterHandles
18239      * @type boolean
18240      * @default false
18241      */
18242     hasOuterHandles: false,
18243
18244     /**
18245      * Code that executes immediately before the startDrag event
18246      * @method b4StartDrag
18247      * @private
18248      */
18249     b4StartDrag: function(x, y) { },
18250
18251     /**
18252      * Abstract method called after a drag/drop object is clicked
18253      * and the drag or mousedown time thresholds have beeen met.
18254      * @method startDrag
18255      * @param {int} X click location
18256      * @param {int} Y click location
18257      */
18258     startDrag: function(x, y) { /* override this */ },
18259
18260     /**
18261      * Code that executes immediately before the onDrag event
18262      * @method b4Drag
18263      * @private
18264      */
18265     b4Drag: function(e) { },
18266
18267     /**
18268      * Abstract method called during the onMouseMove event while dragging an
18269      * object.
18270      * @method onDrag
18271      * @param {Event} e the mousemove event
18272      */
18273     onDrag: function(e) { /* override this */ },
18274
18275     /**
18276      * Abstract method called when this element fist begins hovering over
18277      * another DragDrop obj
18278      * @method onDragEnter
18279      * @param {Event} e the mousemove event
18280      * @param {String|DragDrop[]} id In POINT mode, the element
18281      * id this is hovering over.  In INTERSECT mode, an array of one or more
18282      * dragdrop items being hovered over.
18283      */
18284     onDragEnter: function(e, id) { /* override this */ },
18285
18286     /**
18287      * Code that executes immediately before the onDragOver event
18288      * @method b4DragOver
18289      * @private
18290      */
18291     b4DragOver: function(e) { },
18292
18293     /**
18294      * Abstract method called when this element is hovering over another
18295      * DragDrop obj
18296      * @method onDragOver
18297      * @param {Event} e the mousemove event
18298      * @param {String|DragDrop[]} id In POINT mode, the element
18299      * id this is hovering over.  In INTERSECT mode, an array of dd items
18300      * being hovered over.
18301      */
18302     onDragOver: function(e, id) { /* override this */ },
18303
18304     /**
18305      * Code that executes immediately before the onDragOut event
18306      * @method b4DragOut
18307      * @private
18308      */
18309     b4DragOut: function(e) { },
18310
18311     /**
18312      * Abstract method called when we are no longer hovering over an element
18313      * @method onDragOut
18314      * @param {Event} e the mousemove event
18315      * @param {String|DragDrop[]} id In POINT mode, the element
18316      * id this was hovering over.  In INTERSECT mode, an array of dd items
18317      * that the mouse is no longer over.
18318      */
18319     onDragOut: function(e, id) { /* override this */ },
18320
18321     /**
18322      * Code that executes immediately before the onDragDrop event
18323      * @method b4DragDrop
18324      * @private
18325      */
18326     b4DragDrop: function(e) { },
18327
18328     /**
18329      * Abstract method called when this item is dropped on another DragDrop
18330      * obj
18331      * @method onDragDrop
18332      * @param {Event} e the mouseup event
18333      * @param {String|DragDrop[]} id In POINT mode, the element
18334      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18335      * was dropped on.
18336      */
18337     onDragDrop: function(e, id) { /* override this */ },
18338
18339     /**
18340      * Abstract method called when this item is dropped on an area with no
18341      * drop target
18342      * @method onInvalidDrop
18343      * @param {Event} e the mouseup event
18344      */
18345     onInvalidDrop: function(e) { /* override this */ },
18346
18347     /**
18348      * Code that executes immediately before the endDrag event
18349      * @method b4EndDrag
18350      * @private
18351      */
18352     b4EndDrag: function(e) { },
18353
18354     /**
18355      * Fired when we are done dragging the object
18356      * @method endDrag
18357      * @param {Event} e the mouseup event
18358      */
18359     endDrag: function(e) { /* override this */ },
18360
18361     /**
18362      * Code executed immediately before the onMouseDown event
18363      * @method b4MouseDown
18364      * @param {Event} e the mousedown event
18365      * @private
18366      */
18367     b4MouseDown: function(e) {  },
18368
18369     /**
18370      * Event handler that fires when a drag/drop obj gets a mousedown
18371      * @method onMouseDown
18372      * @param {Event} e the mousedown event
18373      */
18374     onMouseDown: function(e) { /* override this */ },
18375
18376     /**
18377      * Event handler that fires when a drag/drop obj gets a mouseup
18378      * @method onMouseUp
18379      * @param {Event} e the mouseup event
18380      */
18381     onMouseUp: function(e) { /* override this */ },
18382
18383     /**
18384      * Override the onAvailable method to do what is needed after the initial
18385      * position was determined.
18386      * @method onAvailable
18387      */
18388     onAvailable: function () {
18389     },
18390
18391     /*
18392      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18393      * @type Object
18394      */
18395     defaultPadding : {left:0, right:0, top:0, bottom:0},
18396
18397     /*
18398      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18399  *
18400  * Usage:
18401  <pre><code>
18402  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18403                 { dragElId: "existingProxyDiv" });
18404  dd.startDrag = function(){
18405      this.constrainTo("parent-id");
18406  };
18407  </code></pre>
18408  * Or you can initalize it using the {@link Roo.Element} object:
18409  <pre><code>
18410  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18411      startDrag : function(){
18412          this.constrainTo("parent-id");
18413      }
18414  });
18415  </code></pre>
18416      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18417      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18418      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18419      * an object containing the sides to pad. For example: {right:10, bottom:10}
18420      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18421      */
18422     constrainTo : function(constrainTo, pad, inContent){
18423         if(typeof pad == "number"){
18424             pad = {left: pad, right:pad, top:pad, bottom:pad};
18425         }
18426         pad = pad || this.defaultPadding;
18427         var b = Roo.get(this.getEl()).getBox();
18428         var ce = Roo.get(constrainTo);
18429         var s = ce.getScroll();
18430         var c, cd = ce.dom;
18431         if(cd == document.body){
18432             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18433         }else{
18434             xy = ce.getXY();
18435             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18436         }
18437
18438
18439         var topSpace = b.y - c.y;
18440         var leftSpace = b.x - c.x;
18441
18442         this.resetConstraints();
18443         this.setXConstraint(leftSpace - (pad.left||0), // left
18444                 c.width - leftSpace - b.width - (pad.right||0) //right
18445         );
18446         this.setYConstraint(topSpace - (pad.top||0), //top
18447                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18448         );
18449     },
18450
18451     /**
18452      * Returns a reference to the linked element
18453      * @method getEl
18454      * @return {HTMLElement} the html element
18455      */
18456     getEl: function() {
18457         if (!this._domRef) {
18458             this._domRef = Roo.getDom(this.id);
18459         }
18460
18461         return this._domRef;
18462     },
18463
18464     /**
18465      * Returns a reference to the actual element to drag.  By default this is
18466      * the same as the html element, but it can be assigned to another
18467      * element. An example of this can be found in Roo.dd.DDProxy
18468      * @method getDragEl
18469      * @return {HTMLElement} the html element
18470      */
18471     getDragEl: function() {
18472         return Roo.getDom(this.dragElId);
18473     },
18474
18475     /**
18476      * Sets up the DragDrop object.  Must be called in the constructor of any
18477      * Roo.dd.DragDrop subclass
18478      * @method init
18479      * @param id the id of the linked element
18480      * @param {String} sGroup the group of related items
18481      * @param {object} config configuration attributes
18482      */
18483     init: function(id, sGroup, config) {
18484         this.initTarget(id, sGroup, config);
18485         if (!Roo.isTouch) {
18486             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18487         }
18488         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18489         // Event.on(this.id, "selectstart", Event.preventDefault);
18490     },
18491
18492     /**
18493      * Initializes Targeting functionality only... the object does not
18494      * get a mousedown handler.
18495      * @method initTarget
18496      * @param id the id of the linked element
18497      * @param {String} sGroup the group of related items
18498      * @param {object} config configuration attributes
18499      */
18500     initTarget: function(id, sGroup, config) {
18501
18502         // configuration attributes
18503         this.config = config || {};
18504
18505         // create a local reference to the drag and drop manager
18506         this.DDM = Roo.dd.DDM;
18507         // initialize the groups array
18508         this.groups = {};
18509
18510         // assume that we have an element reference instead of an id if the
18511         // parameter is not a string
18512         if (typeof id !== "string") {
18513             id = Roo.id(id);
18514         }
18515
18516         // set the id
18517         this.id = id;
18518
18519         // add to an interaction group
18520         this.addToGroup((sGroup) ? sGroup : "default");
18521
18522         // We don't want to register this as the handle with the manager
18523         // so we just set the id rather than calling the setter.
18524         this.handleElId = id;
18525
18526         // the linked element is the element that gets dragged by default
18527         this.setDragElId(id);
18528
18529         // by default, clicked anchors will not start drag operations.
18530         this.invalidHandleTypes = { A: "A" };
18531         this.invalidHandleIds = {};
18532         this.invalidHandleClasses = [];
18533
18534         this.applyConfig();
18535
18536         this.handleOnAvailable();
18537     },
18538
18539     /**
18540      * Applies the configuration parameters that were passed into the constructor.
18541      * This is supposed to happen at each level through the inheritance chain.  So
18542      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18543      * DragDrop in order to get all of the parameters that are available in
18544      * each object.
18545      * @method applyConfig
18546      */
18547     applyConfig: function() {
18548
18549         // configurable properties:
18550         //    padding, isTarget, maintainOffset, primaryButtonOnly
18551         this.padding           = this.config.padding || [0, 0, 0, 0];
18552         this.isTarget          = (this.config.isTarget !== false);
18553         this.maintainOffset    = (this.config.maintainOffset);
18554         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18555
18556     },
18557
18558     /**
18559      * Executed when the linked element is available
18560      * @method handleOnAvailable
18561      * @private
18562      */
18563     handleOnAvailable: function() {
18564         this.available = true;
18565         this.resetConstraints();
18566         this.onAvailable();
18567     },
18568
18569      /**
18570      * Configures the padding for the target zone in px.  Effectively expands
18571      * (or reduces) the virtual object size for targeting calculations.
18572      * Supports css-style shorthand; if only one parameter is passed, all sides
18573      * will have that padding, and if only two are passed, the top and bottom
18574      * will have the first param, the left and right the second.
18575      * @method setPadding
18576      * @param {int} iTop    Top pad
18577      * @param {int} iRight  Right pad
18578      * @param {int} iBot    Bot pad
18579      * @param {int} iLeft   Left pad
18580      */
18581     setPadding: function(iTop, iRight, iBot, iLeft) {
18582         // this.padding = [iLeft, iRight, iTop, iBot];
18583         if (!iRight && 0 !== iRight) {
18584             this.padding = [iTop, iTop, iTop, iTop];
18585         } else if (!iBot && 0 !== iBot) {
18586             this.padding = [iTop, iRight, iTop, iRight];
18587         } else {
18588             this.padding = [iTop, iRight, iBot, iLeft];
18589         }
18590     },
18591
18592     /**
18593      * Stores the initial placement of the linked element.
18594      * @method setInitialPosition
18595      * @param {int} diffX   the X offset, default 0
18596      * @param {int} diffY   the Y offset, default 0
18597      */
18598     setInitPosition: function(diffX, diffY) {
18599         var el = this.getEl();
18600
18601         if (!this.DDM.verifyEl(el)) {
18602             return;
18603         }
18604
18605         var dx = diffX || 0;
18606         var dy = diffY || 0;
18607
18608         var p = Dom.getXY( el );
18609
18610         this.initPageX = p[0] - dx;
18611         this.initPageY = p[1] - dy;
18612
18613         this.lastPageX = p[0];
18614         this.lastPageY = p[1];
18615
18616
18617         this.setStartPosition(p);
18618     },
18619
18620     /**
18621      * Sets the start position of the element.  This is set when the obj
18622      * is initialized, the reset when a drag is started.
18623      * @method setStartPosition
18624      * @param pos current position (from previous lookup)
18625      * @private
18626      */
18627     setStartPosition: function(pos) {
18628         var p = pos || Dom.getXY( this.getEl() );
18629         this.deltaSetXY = null;
18630
18631         this.startPageX = p[0];
18632         this.startPageY = p[1];
18633     },
18634
18635     /**
18636      * Add this instance to a group of related drag/drop objects.  All
18637      * instances belong to at least one group, and can belong to as many
18638      * groups as needed.
18639      * @method addToGroup
18640      * @param sGroup {string} the name of the group
18641      */
18642     addToGroup: function(sGroup) {
18643         this.groups[sGroup] = true;
18644         this.DDM.regDragDrop(this, sGroup);
18645     },
18646
18647     /**
18648      * Remove's this instance from the supplied interaction group
18649      * @method removeFromGroup
18650      * @param {string}  sGroup  The group to drop
18651      */
18652     removeFromGroup: function(sGroup) {
18653         if (this.groups[sGroup]) {
18654             delete this.groups[sGroup];
18655         }
18656
18657         this.DDM.removeDDFromGroup(this, sGroup);
18658     },
18659
18660     /**
18661      * Allows you to specify that an element other than the linked element
18662      * will be moved with the cursor during a drag
18663      * @method setDragElId
18664      * @param id {string} the id of the element that will be used to initiate the drag
18665      */
18666     setDragElId: function(id) {
18667         this.dragElId = id;
18668     },
18669
18670     /**
18671      * Allows you to specify a child of the linked element that should be
18672      * used to initiate the drag operation.  An example of this would be if
18673      * you have a content div with text and links.  Clicking anywhere in the
18674      * content area would normally start the drag operation.  Use this method
18675      * to specify that an element inside of the content div is the element
18676      * that starts the drag operation.
18677      * @method setHandleElId
18678      * @param id {string} the id of the element that will be used to
18679      * initiate the drag.
18680      */
18681     setHandleElId: function(id) {
18682         if (typeof id !== "string") {
18683             id = Roo.id(id);
18684         }
18685         this.handleElId = id;
18686         this.DDM.regHandle(this.id, id);
18687     },
18688
18689     /**
18690      * Allows you to set an element outside of the linked element as a drag
18691      * handle
18692      * @method setOuterHandleElId
18693      * @param id the id of the element that will be used to initiate the drag
18694      */
18695     setOuterHandleElId: function(id) {
18696         if (typeof id !== "string") {
18697             id = Roo.id(id);
18698         }
18699         Event.on(id, "mousedown",
18700                 this.handleMouseDown, this);
18701         this.setHandleElId(id);
18702
18703         this.hasOuterHandles = true;
18704     },
18705
18706     /**
18707      * Remove all drag and drop hooks for this element
18708      * @method unreg
18709      */
18710     unreg: function() {
18711         Event.un(this.id, "mousedown",
18712                 this.handleMouseDown);
18713         Event.un(this.id, "touchstart",
18714                 this.handleMouseDown);
18715         this._domRef = null;
18716         this.DDM._remove(this);
18717     },
18718
18719     destroy : function(){
18720         this.unreg();
18721     },
18722
18723     /**
18724      * Returns true if this instance is locked, or the drag drop mgr is locked
18725      * (meaning that all drag/drop is disabled on the page.)
18726      * @method isLocked
18727      * @return {boolean} true if this obj or all drag/drop is locked, else
18728      * false
18729      */
18730     isLocked: function() {
18731         return (this.DDM.isLocked() || this.locked);
18732     },
18733
18734     /**
18735      * Fired when this object is clicked
18736      * @method handleMouseDown
18737      * @param {Event} e
18738      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18739      * @private
18740      */
18741     handleMouseDown: function(e, oDD){
18742      
18743         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18744             //Roo.log('not touch/ button !=0');
18745             return;
18746         }
18747         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18748             return; // double touch..
18749         }
18750         
18751
18752         if (this.isLocked()) {
18753             //Roo.log('locked');
18754             return;
18755         }
18756
18757         this.DDM.refreshCache(this.groups);
18758 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18759         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18760         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18761             //Roo.log('no outer handes or not over target');
18762                 // do nothing.
18763         } else {
18764 //            Roo.log('check validator');
18765             if (this.clickValidator(e)) {
18766 //                Roo.log('validate success');
18767                 // set the initial element position
18768                 this.setStartPosition();
18769
18770
18771                 this.b4MouseDown(e);
18772                 this.onMouseDown(e);
18773
18774                 this.DDM.handleMouseDown(e, this);
18775
18776                 this.DDM.stopEvent(e);
18777             } else {
18778
18779
18780             }
18781         }
18782     },
18783
18784     clickValidator: function(e) {
18785         var target = e.getTarget();
18786         return ( this.isValidHandleChild(target) &&
18787                     (this.id == this.handleElId ||
18788                         this.DDM.handleWasClicked(target, this.id)) );
18789     },
18790
18791     /**
18792      * Allows you to specify a tag name that should not start a drag operation
18793      * when clicked.  This is designed to facilitate embedding links within a
18794      * drag handle that do something other than start the drag.
18795      * @method addInvalidHandleType
18796      * @param {string} tagName the type of element to exclude
18797      */
18798     addInvalidHandleType: function(tagName) {
18799         var type = tagName.toUpperCase();
18800         this.invalidHandleTypes[type] = type;
18801     },
18802
18803     /**
18804      * Lets you to specify an element id for a child of a drag handle
18805      * that should not initiate a drag
18806      * @method addInvalidHandleId
18807      * @param {string} id the element id of the element you wish to ignore
18808      */
18809     addInvalidHandleId: function(id) {
18810         if (typeof id !== "string") {
18811             id = Roo.id(id);
18812         }
18813         this.invalidHandleIds[id] = id;
18814     },
18815
18816     /**
18817      * Lets you specify a css class of elements that will not initiate a drag
18818      * @method addInvalidHandleClass
18819      * @param {string} cssClass the class of the elements you wish to ignore
18820      */
18821     addInvalidHandleClass: function(cssClass) {
18822         this.invalidHandleClasses.push(cssClass);
18823     },
18824
18825     /**
18826      * Unsets an excluded tag name set by addInvalidHandleType
18827      * @method removeInvalidHandleType
18828      * @param {string} tagName the type of element to unexclude
18829      */
18830     removeInvalidHandleType: function(tagName) {
18831         var type = tagName.toUpperCase();
18832         // this.invalidHandleTypes[type] = null;
18833         delete this.invalidHandleTypes[type];
18834     },
18835
18836     /**
18837      * Unsets an invalid handle id
18838      * @method removeInvalidHandleId
18839      * @param {string} id the id of the element to re-enable
18840      */
18841     removeInvalidHandleId: function(id) {
18842         if (typeof id !== "string") {
18843             id = Roo.id(id);
18844         }
18845         delete this.invalidHandleIds[id];
18846     },
18847
18848     /**
18849      * Unsets an invalid css class
18850      * @method removeInvalidHandleClass
18851      * @param {string} cssClass the class of the element(s) you wish to
18852      * re-enable
18853      */
18854     removeInvalidHandleClass: function(cssClass) {
18855         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18856             if (this.invalidHandleClasses[i] == cssClass) {
18857                 delete this.invalidHandleClasses[i];
18858             }
18859         }
18860     },
18861
18862     /**
18863      * Checks the tag exclusion list to see if this click should be ignored
18864      * @method isValidHandleChild
18865      * @param {HTMLElement} node the HTMLElement to evaluate
18866      * @return {boolean} true if this is a valid tag type, false if not
18867      */
18868     isValidHandleChild: function(node) {
18869
18870         var valid = true;
18871         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18872         var nodeName;
18873         try {
18874             nodeName = node.nodeName.toUpperCase();
18875         } catch(e) {
18876             nodeName = node.nodeName;
18877         }
18878         valid = valid && !this.invalidHandleTypes[nodeName];
18879         valid = valid && !this.invalidHandleIds[node.id];
18880
18881         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18882             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18883         }
18884
18885
18886         return valid;
18887
18888     },
18889
18890     /**
18891      * Create the array of horizontal tick marks if an interval was specified
18892      * in setXConstraint().
18893      * @method setXTicks
18894      * @private
18895      */
18896     setXTicks: function(iStartX, iTickSize) {
18897         this.xTicks = [];
18898         this.xTickSize = iTickSize;
18899
18900         var tickMap = {};
18901
18902         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18903             if (!tickMap[i]) {
18904                 this.xTicks[this.xTicks.length] = i;
18905                 tickMap[i] = true;
18906             }
18907         }
18908
18909         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18910             if (!tickMap[i]) {
18911                 this.xTicks[this.xTicks.length] = i;
18912                 tickMap[i] = true;
18913             }
18914         }
18915
18916         this.xTicks.sort(this.DDM.numericSort) ;
18917     },
18918
18919     /**
18920      * Create the array of vertical tick marks if an interval was specified in
18921      * setYConstraint().
18922      * @method setYTicks
18923      * @private
18924      */
18925     setYTicks: function(iStartY, iTickSize) {
18926         this.yTicks = [];
18927         this.yTickSize = iTickSize;
18928
18929         var tickMap = {};
18930
18931         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18932             if (!tickMap[i]) {
18933                 this.yTicks[this.yTicks.length] = i;
18934                 tickMap[i] = true;
18935             }
18936         }
18937
18938         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18939             if (!tickMap[i]) {
18940                 this.yTicks[this.yTicks.length] = i;
18941                 tickMap[i] = true;
18942             }
18943         }
18944
18945         this.yTicks.sort(this.DDM.numericSort) ;
18946     },
18947
18948     /**
18949      * By default, the element can be dragged any place on the screen.  Use
18950      * this method to limit the horizontal travel of the element.  Pass in
18951      * 0,0 for the parameters if you want to lock the drag to the y axis.
18952      * @method setXConstraint
18953      * @param {int} iLeft the number of pixels the element can move to the left
18954      * @param {int} iRight the number of pixels the element can move to the
18955      * right
18956      * @param {int} iTickSize optional parameter for specifying that the
18957      * element
18958      * should move iTickSize pixels at a time.
18959      */
18960     setXConstraint: function(iLeft, iRight, iTickSize) {
18961         this.leftConstraint = iLeft;
18962         this.rightConstraint = iRight;
18963
18964         this.minX = this.initPageX - iLeft;
18965         this.maxX = this.initPageX + iRight;
18966         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
18967
18968         this.constrainX = true;
18969     },
18970
18971     /**
18972      * Clears any constraints applied to this instance.  Also clears ticks
18973      * since they can't exist independent of a constraint at this time.
18974      * @method clearConstraints
18975      */
18976     clearConstraints: function() {
18977         this.constrainX = false;
18978         this.constrainY = false;
18979         this.clearTicks();
18980     },
18981
18982     /**
18983      * Clears any tick interval defined for this instance
18984      * @method clearTicks
18985      */
18986     clearTicks: function() {
18987         this.xTicks = null;
18988         this.yTicks = null;
18989         this.xTickSize = 0;
18990         this.yTickSize = 0;
18991     },
18992
18993     /**
18994      * By default, the element can be dragged any place on the screen.  Set
18995      * this to limit the vertical travel of the element.  Pass in 0,0 for the
18996      * parameters if you want to lock the drag to the x axis.
18997      * @method setYConstraint
18998      * @param {int} iUp the number of pixels the element can move up
18999      * @param {int} iDown the number of pixels the element can move down
19000      * @param {int} iTickSize optional parameter for specifying that the
19001      * element should move iTickSize pixels at a time.
19002      */
19003     setYConstraint: function(iUp, iDown, iTickSize) {
19004         this.topConstraint = iUp;
19005         this.bottomConstraint = iDown;
19006
19007         this.minY = this.initPageY - iUp;
19008         this.maxY = this.initPageY + iDown;
19009         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19010
19011         this.constrainY = true;
19012
19013     },
19014
19015     /**
19016      * resetConstraints must be called if you manually reposition a dd element.
19017      * @method resetConstraints
19018      * @param {boolean} maintainOffset
19019      */
19020     resetConstraints: function() {
19021
19022
19023         // Maintain offsets if necessary
19024         if (this.initPageX || this.initPageX === 0) {
19025             // figure out how much this thing has moved
19026             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19027             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19028
19029             this.setInitPosition(dx, dy);
19030
19031         // This is the first time we have detected the element's position
19032         } else {
19033             this.setInitPosition();
19034         }
19035
19036         if (this.constrainX) {
19037             this.setXConstraint( this.leftConstraint,
19038                                  this.rightConstraint,
19039                                  this.xTickSize        );
19040         }
19041
19042         if (this.constrainY) {
19043             this.setYConstraint( this.topConstraint,
19044                                  this.bottomConstraint,
19045                                  this.yTickSize         );
19046         }
19047     },
19048
19049     /**
19050      * Normally the drag element is moved pixel by pixel, but we can specify
19051      * that it move a number of pixels at a time.  This method resolves the
19052      * location when we have it set up like this.
19053      * @method getTick
19054      * @param {int} val where we want to place the object
19055      * @param {int[]} tickArray sorted array of valid points
19056      * @return {int} the closest tick
19057      * @private
19058      */
19059     getTick: function(val, tickArray) {
19060
19061         if (!tickArray) {
19062             // If tick interval is not defined, it is effectively 1 pixel,
19063             // so we return the value passed to us.
19064             return val;
19065         } else if (tickArray[0] >= val) {
19066             // The value is lower than the first tick, so we return the first
19067             // tick.
19068             return tickArray[0];
19069         } else {
19070             for (var i=0, len=tickArray.length; i<len; ++i) {
19071                 var next = i + 1;
19072                 if (tickArray[next] && tickArray[next] >= val) {
19073                     var diff1 = val - tickArray[i];
19074                     var diff2 = tickArray[next] - val;
19075                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19076                 }
19077             }
19078
19079             // The value is larger than the last tick, so we return the last
19080             // tick.
19081             return tickArray[tickArray.length - 1];
19082         }
19083     },
19084
19085     /**
19086      * toString method
19087      * @method toString
19088      * @return {string} string representation of the dd obj
19089      */
19090     toString: function() {
19091         return ("DragDrop " + this.id);
19092     }
19093
19094 });
19095
19096 })();
19097 /*
19098  * Based on:
19099  * Ext JS Library 1.1.1
19100  * Copyright(c) 2006-2007, Ext JS, LLC.
19101  *
19102  * Originally Released Under LGPL - original licence link has changed is not relivant.
19103  *
19104  * Fork - LGPL
19105  * <script type="text/javascript">
19106  */
19107
19108
19109 /**
19110  * The drag and drop utility provides a framework for building drag and drop
19111  * applications.  In addition to enabling drag and drop for specific elements,
19112  * the drag and drop elements are tracked by the manager class, and the
19113  * interactions between the various elements are tracked during the drag and
19114  * the implementing code is notified about these important moments.
19115  */
19116
19117 // Only load the library once.  Rewriting the manager class would orphan
19118 // existing drag and drop instances.
19119 if (!Roo.dd.DragDropMgr) {
19120
19121 /**
19122  * @class Roo.dd.DragDropMgr
19123  * DragDropMgr is a singleton that tracks the element interaction for
19124  * all DragDrop items in the window.  Generally, you will not call
19125  * this class directly, but it does have helper methods that could
19126  * be useful in your DragDrop implementations.
19127  * @singleton
19128  */
19129 Roo.dd.DragDropMgr = function() {
19130
19131     var Event = Roo.EventManager;
19132
19133     return {
19134
19135         /**
19136          * Two dimensional Array of registered DragDrop objects.  The first
19137          * dimension is the DragDrop item group, the second the DragDrop
19138          * object.
19139          * @property ids
19140          * @type {string: string}
19141          * @private
19142          * @static
19143          */
19144         ids: {},
19145
19146         /**
19147          * Array of element ids defined as drag handles.  Used to determine
19148          * if the element that generated the mousedown event is actually the
19149          * handle and not the html element itself.
19150          * @property handleIds
19151          * @type {string: string}
19152          * @private
19153          * @static
19154          */
19155         handleIds: {},
19156
19157         /**
19158          * the DragDrop object that is currently being dragged
19159          * @property dragCurrent
19160          * @type DragDrop
19161          * @private
19162          * @static
19163          **/
19164         dragCurrent: null,
19165
19166         /**
19167          * the DragDrop object(s) that are being hovered over
19168          * @property dragOvers
19169          * @type Array
19170          * @private
19171          * @static
19172          */
19173         dragOvers: {},
19174
19175         /**
19176          * the X distance between the cursor and the object being dragged
19177          * @property deltaX
19178          * @type int
19179          * @private
19180          * @static
19181          */
19182         deltaX: 0,
19183
19184         /**
19185          * the Y distance between the cursor and the object being dragged
19186          * @property deltaY
19187          * @type int
19188          * @private
19189          * @static
19190          */
19191         deltaY: 0,
19192
19193         /**
19194          * Flag to determine if we should prevent the default behavior of the
19195          * events we define. By default this is true, but this can be set to
19196          * false if you need the default behavior (not recommended)
19197          * @property preventDefault
19198          * @type boolean
19199          * @static
19200          */
19201         preventDefault: true,
19202
19203         /**
19204          * Flag to determine if we should stop the propagation of the events
19205          * we generate. This is true by default but you may want to set it to
19206          * false if the html element contains other features that require the
19207          * mouse click.
19208          * @property stopPropagation
19209          * @type boolean
19210          * @static
19211          */
19212         stopPropagation: true,
19213
19214         /**
19215          * Internal flag that is set to true when drag and drop has been
19216          * intialized
19217          * @property initialized
19218          * @private
19219          * @static
19220          */
19221         initalized: false,
19222
19223         /**
19224          * All drag and drop can be disabled.
19225          * @property locked
19226          * @private
19227          * @static
19228          */
19229         locked: false,
19230
19231         /**
19232          * Called the first time an element is registered.
19233          * @method init
19234          * @private
19235          * @static
19236          */
19237         init: function() {
19238             this.initialized = true;
19239         },
19240
19241         /**
19242          * In point mode, drag and drop interaction is defined by the
19243          * location of the cursor during the drag/drop
19244          * @property POINT
19245          * @type int
19246          * @static
19247          */
19248         POINT: 0,
19249
19250         /**
19251          * In intersect mode, drag and drop interactio nis defined by the
19252          * overlap of two or more drag and drop objects.
19253          * @property INTERSECT
19254          * @type int
19255          * @static
19256          */
19257         INTERSECT: 1,
19258
19259         /**
19260          * The current drag and drop mode.  Default: POINT
19261          * @property mode
19262          * @type int
19263          * @static
19264          */
19265         mode: 0,
19266
19267         /**
19268          * Runs method on all drag and drop objects
19269          * @method _execOnAll
19270          * @private
19271          * @static
19272          */
19273         _execOnAll: function(sMethod, args) {
19274             for (var i in this.ids) {
19275                 for (var j in this.ids[i]) {
19276                     var oDD = this.ids[i][j];
19277                     if (! this.isTypeOfDD(oDD)) {
19278                         continue;
19279                     }
19280                     oDD[sMethod].apply(oDD, args);
19281                 }
19282             }
19283         },
19284
19285         /**
19286          * Drag and drop initialization.  Sets up the global event handlers
19287          * @method _onLoad
19288          * @private
19289          * @static
19290          */
19291         _onLoad: function() {
19292
19293             this.init();
19294
19295             if (!Roo.isTouch) {
19296                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19297                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19298             }
19299             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19300             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19301             
19302             Event.on(window,   "unload",    this._onUnload, this, true);
19303             Event.on(window,   "resize",    this._onResize, this, true);
19304             // Event.on(window,   "mouseout",    this._test);
19305
19306         },
19307
19308         /**
19309          * Reset constraints on all drag and drop objs
19310          * @method _onResize
19311          * @private
19312          * @static
19313          */
19314         _onResize: function(e) {
19315             this._execOnAll("resetConstraints", []);
19316         },
19317
19318         /**
19319          * Lock all drag and drop functionality
19320          * @method lock
19321          * @static
19322          */
19323         lock: function() { this.locked = true; },
19324
19325         /**
19326          * Unlock all drag and drop functionality
19327          * @method unlock
19328          * @static
19329          */
19330         unlock: function() { this.locked = false; },
19331
19332         /**
19333          * Is drag and drop locked?
19334          * @method isLocked
19335          * @return {boolean} True if drag and drop is locked, false otherwise.
19336          * @static
19337          */
19338         isLocked: function() { return this.locked; },
19339
19340         /**
19341          * Location cache that is set for all drag drop objects when a drag is
19342          * initiated, cleared when the drag is finished.
19343          * @property locationCache
19344          * @private
19345          * @static
19346          */
19347         locationCache: {},
19348
19349         /**
19350          * Set useCache to false if you want to force object the lookup of each
19351          * drag and drop linked element constantly during a drag.
19352          * @property useCache
19353          * @type boolean
19354          * @static
19355          */
19356         useCache: true,
19357
19358         /**
19359          * The number of pixels that the mouse needs to move after the
19360          * mousedown before the drag is initiated.  Default=3;
19361          * @property clickPixelThresh
19362          * @type int
19363          * @static
19364          */
19365         clickPixelThresh: 3,
19366
19367         /**
19368          * The number of milliseconds after the mousedown event to initiate the
19369          * drag if we don't get a mouseup event. Default=1000
19370          * @property clickTimeThresh
19371          * @type int
19372          * @static
19373          */
19374         clickTimeThresh: 350,
19375
19376         /**
19377          * Flag that indicates that either the drag pixel threshold or the
19378          * mousdown time threshold has been met
19379          * @property dragThreshMet
19380          * @type boolean
19381          * @private
19382          * @static
19383          */
19384         dragThreshMet: false,
19385
19386         /**
19387          * Timeout used for the click time threshold
19388          * @property clickTimeout
19389          * @type Object
19390          * @private
19391          * @static
19392          */
19393         clickTimeout: null,
19394
19395         /**
19396          * The X position of the mousedown event stored for later use when a
19397          * drag threshold is met.
19398          * @property startX
19399          * @type int
19400          * @private
19401          * @static
19402          */
19403         startX: 0,
19404
19405         /**
19406          * The Y position of the mousedown event stored for later use when a
19407          * drag threshold is met.
19408          * @property startY
19409          * @type int
19410          * @private
19411          * @static
19412          */
19413         startY: 0,
19414
19415         /**
19416          * Each DragDrop instance must be registered with the DragDropMgr.
19417          * This is executed in DragDrop.init()
19418          * @method regDragDrop
19419          * @param {DragDrop} oDD the DragDrop object to register
19420          * @param {String} sGroup the name of the group this element belongs to
19421          * @static
19422          */
19423         regDragDrop: function(oDD, sGroup) {
19424             if (!this.initialized) { this.init(); }
19425
19426             if (!this.ids[sGroup]) {
19427                 this.ids[sGroup] = {};
19428             }
19429             this.ids[sGroup][oDD.id] = oDD;
19430         },
19431
19432         /**
19433          * Removes the supplied dd instance from the supplied group. Executed
19434          * by DragDrop.removeFromGroup, so don't call this function directly.
19435          * @method removeDDFromGroup
19436          * @private
19437          * @static
19438          */
19439         removeDDFromGroup: function(oDD, sGroup) {
19440             if (!this.ids[sGroup]) {
19441                 this.ids[sGroup] = {};
19442             }
19443
19444             var obj = this.ids[sGroup];
19445             if (obj && obj[oDD.id]) {
19446                 delete obj[oDD.id];
19447             }
19448         },
19449
19450         /**
19451          * Unregisters a drag and drop item.  This is executed in
19452          * DragDrop.unreg, use that method instead of calling this directly.
19453          * @method _remove
19454          * @private
19455          * @static
19456          */
19457         _remove: function(oDD) {
19458             for (var g in oDD.groups) {
19459                 if (g && this.ids[g][oDD.id]) {
19460                     delete this.ids[g][oDD.id];
19461                 }
19462             }
19463             delete this.handleIds[oDD.id];
19464         },
19465
19466         /**
19467          * Each DragDrop handle element must be registered.  This is done
19468          * automatically when executing DragDrop.setHandleElId()
19469          * @method regHandle
19470          * @param {String} sDDId the DragDrop id this element is a handle for
19471          * @param {String} sHandleId the id of the element that is the drag
19472          * handle
19473          * @static
19474          */
19475         regHandle: function(sDDId, sHandleId) {
19476             if (!this.handleIds[sDDId]) {
19477                 this.handleIds[sDDId] = {};
19478             }
19479             this.handleIds[sDDId][sHandleId] = sHandleId;
19480         },
19481
19482         /**
19483          * Utility function to determine if a given element has been
19484          * registered as a drag drop item.
19485          * @method isDragDrop
19486          * @param {String} id the element id to check
19487          * @return {boolean} true if this element is a DragDrop item,
19488          * false otherwise
19489          * @static
19490          */
19491         isDragDrop: function(id) {
19492             return ( this.getDDById(id) ) ? true : false;
19493         },
19494
19495         /**
19496          * Returns the drag and drop instances that are in all groups the
19497          * passed in instance belongs to.
19498          * @method getRelated
19499          * @param {DragDrop} p_oDD the obj to get related data for
19500          * @param {boolean} bTargetsOnly if true, only return targetable objs
19501          * @return {DragDrop[]} the related instances
19502          * @static
19503          */
19504         getRelated: function(p_oDD, bTargetsOnly) {
19505             var oDDs = [];
19506             for (var i in p_oDD.groups) {
19507                 for (j in this.ids[i]) {
19508                     var dd = this.ids[i][j];
19509                     if (! this.isTypeOfDD(dd)) {
19510                         continue;
19511                     }
19512                     if (!bTargetsOnly || dd.isTarget) {
19513                         oDDs[oDDs.length] = dd;
19514                     }
19515                 }
19516             }
19517
19518             return oDDs;
19519         },
19520
19521         /**
19522          * Returns true if the specified dd target is a legal target for
19523          * the specifice drag obj
19524          * @method isLegalTarget
19525          * @param {DragDrop} the drag obj
19526          * @param {DragDrop} the target
19527          * @return {boolean} true if the target is a legal target for the
19528          * dd obj
19529          * @static
19530          */
19531         isLegalTarget: function (oDD, oTargetDD) {
19532             var targets = this.getRelated(oDD, true);
19533             for (var i=0, len=targets.length;i<len;++i) {
19534                 if (targets[i].id == oTargetDD.id) {
19535                     return true;
19536                 }
19537             }
19538
19539             return false;
19540         },
19541
19542         /**
19543          * My goal is to be able to transparently determine if an object is
19544          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19545          * returns "object", oDD.constructor.toString() always returns
19546          * "DragDrop" and not the name of the subclass.  So for now it just
19547          * evaluates a well-known variable in DragDrop.
19548          * @method isTypeOfDD
19549          * @param {Object} the object to evaluate
19550          * @return {boolean} true if typeof oDD = DragDrop
19551          * @static
19552          */
19553         isTypeOfDD: function (oDD) {
19554             return (oDD && oDD.__ygDragDrop);
19555         },
19556
19557         /**
19558          * Utility function to determine if a given element has been
19559          * registered as a drag drop handle for the given Drag Drop object.
19560          * @method isHandle
19561          * @param {String} id the element id to check
19562          * @return {boolean} true if this element is a DragDrop handle, false
19563          * otherwise
19564          * @static
19565          */
19566         isHandle: function(sDDId, sHandleId) {
19567             return ( this.handleIds[sDDId] &&
19568                             this.handleIds[sDDId][sHandleId] );
19569         },
19570
19571         /**
19572          * Returns the DragDrop instance for a given id
19573          * @method getDDById
19574          * @param {String} id the id of the DragDrop object
19575          * @return {DragDrop} the drag drop object, null if it is not found
19576          * @static
19577          */
19578         getDDById: function(id) {
19579             for (var i in this.ids) {
19580                 if (this.ids[i][id]) {
19581                     return this.ids[i][id];
19582                 }
19583             }
19584             return null;
19585         },
19586
19587         /**
19588          * Fired after a registered DragDrop object gets the mousedown event.
19589          * Sets up the events required to track the object being dragged
19590          * @method handleMouseDown
19591          * @param {Event} e the event
19592          * @param oDD the DragDrop object being dragged
19593          * @private
19594          * @static
19595          */
19596         handleMouseDown: function(e, oDD) {
19597             if(Roo.QuickTips){
19598                 Roo.QuickTips.disable();
19599             }
19600             this.currentTarget = e.getTarget();
19601
19602             this.dragCurrent = oDD;
19603
19604             var el = oDD.getEl();
19605
19606             // track start position
19607             this.startX = e.getPageX();
19608             this.startY = e.getPageY();
19609
19610             this.deltaX = this.startX - el.offsetLeft;
19611             this.deltaY = this.startY - el.offsetTop;
19612
19613             this.dragThreshMet = false;
19614
19615             this.clickTimeout = setTimeout(
19616                     function() {
19617                         var DDM = Roo.dd.DDM;
19618                         DDM.startDrag(DDM.startX, DDM.startY);
19619                     },
19620                     this.clickTimeThresh );
19621         },
19622
19623         /**
19624          * Fired when either the drag pixel threshol or the mousedown hold
19625          * time threshold has been met.
19626          * @method startDrag
19627          * @param x {int} the X position of the original mousedown
19628          * @param y {int} the Y position of the original mousedown
19629          * @static
19630          */
19631         startDrag: function(x, y) {
19632             clearTimeout(this.clickTimeout);
19633             if (this.dragCurrent) {
19634                 this.dragCurrent.b4StartDrag(x, y);
19635                 this.dragCurrent.startDrag(x, y);
19636             }
19637             this.dragThreshMet = true;
19638         },
19639
19640         /**
19641          * Internal function to handle the mouseup event.  Will be invoked
19642          * from the context of the document.
19643          * @method handleMouseUp
19644          * @param {Event} e the event
19645          * @private
19646          * @static
19647          */
19648         handleMouseUp: function(e) {
19649
19650             if(Roo.QuickTips){
19651                 Roo.QuickTips.enable();
19652             }
19653             if (! this.dragCurrent) {
19654                 return;
19655             }
19656
19657             clearTimeout(this.clickTimeout);
19658
19659             if (this.dragThreshMet) {
19660                 this.fireEvents(e, true);
19661             } else {
19662             }
19663
19664             this.stopDrag(e);
19665
19666             this.stopEvent(e);
19667         },
19668
19669         /**
19670          * Utility to stop event propagation and event default, if these
19671          * features are turned on.
19672          * @method stopEvent
19673          * @param {Event} e the event as returned by this.getEvent()
19674          * @static
19675          */
19676         stopEvent: function(e){
19677             if(this.stopPropagation) {
19678                 e.stopPropagation();
19679             }
19680
19681             if (this.preventDefault) {
19682                 e.preventDefault();
19683             }
19684         },
19685
19686         /**
19687          * Internal function to clean up event handlers after the drag
19688          * operation is complete
19689          * @method stopDrag
19690          * @param {Event} e the event
19691          * @private
19692          * @static
19693          */
19694         stopDrag: function(e) {
19695             // Fire the drag end event for the item that was dragged
19696             if (this.dragCurrent) {
19697                 if (this.dragThreshMet) {
19698                     this.dragCurrent.b4EndDrag(e);
19699                     this.dragCurrent.endDrag(e);
19700                 }
19701
19702                 this.dragCurrent.onMouseUp(e);
19703             }
19704
19705             this.dragCurrent = null;
19706             this.dragOvers = {};
19707         },
19708
19709         /**
19710          * Internal function to handle the mousemove event.  Will be invoked
19711          * from the context of the html element.
19712          *
19713          * @TODO figure out what we can do about mouse events lost when the
19714          * user drags objects beyond the window boundary.  Currently we can
19715          * detect this in internet explorer by verifying that the mouse is
19716          * down during the mousemove event.  Firefox doesn't give us the
19717          * button state on the mousemove event.
19718          * @method handleMouseMove
19719          * @param {Event} e the event
19720          * @private
19721          * @static
19722          */
19723         handleMouseMove: function(e) {
19724             if (! this.dragCurrent) {
19725                 return true;
19726             }
19727
19728             // var button = e.which || e.button;
19729
19730             // check for IE mouseup outside of page boundary
19731             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19732                 this.stopEvent(e);
19733                 return this.handleMouseUp(e);
19734             }
19735
19736             if (!this.dragThreshMet) {
19737                 var diffX = Math.abs(this.startX - e.getPageX());
19738                 var diffY = Math.abs(this.startY - e.getPageY());
19739                 if (diffX > this.clickPixelThresh ||
19740                             diffY > this.clickPixelThresh) {
19741                     this.startDrag(this.startX, this.startY);
19742                 }
19743             }
19744
19745             if (this.dragThreshMet) {
19746                 this.dragCurrent.b4Drag(e);
19747                 this.dragCurrent.onDrag(e);
19748                 if(!this.dragCurrent.moveOnly){
19749                     this.fireEvents(e, false);
19750                 }
19751             }
19752
19753             this.stopEvent(e);
19754
19755             return true;
19756         },
19757
19758         /**
19759          * Iterates over all of the DragDrop elements to find ones we are
19760          * hovering over or dropping on
19761          * @method fireEvents
19762          * @param {Event} e the event
19763          * @param {boolean} isDrop is this a drop op or a mouseover op?
19764          * @private
19765          * @static
19766          */
19767         fireEvents: function(e, isDrop) {
19768             var dc = this.dragCurrent;
19769
19770             // If the user did the mouse up outside of the window, we could
19771             // get here even though we have ended the drag.
19772             if (!dc || dc.isLocked()) {
19773                 return;
19774             }
19775
19776             var pt = e.getPoint();
19777
19778             // cache the previous dragOver array
19779             var oldOvers = [];
19780
19781             var outEvts   = [];
19782             var overEvts  = [];
19783             var dropEvts  = [];
19784             var enterEvts = [];
19785
19786             // Check to see if the object(s) we were hovering over is no longer
19787             // being hovered over so we can fire the onDragOut event
19788             for (var i in this.dragOvers) {
19789
19790                 var ddo = this.dragOvers[i];
19791
19792                 if (! this.isTypeOfDD(ddo)) {
19793                     continue;
19794                 }
19795
19796                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19797                     outEvts.push( ddo );
19798                 }
19799
19800                 oldOvers[i] = true;
19801                 delete this.dragOvers[i];
19802             }
19803
19804             for (var sGroup in dc.groups) {
19805
19806                 if ("string" != typeof sGroup) {
19807                     continue;
19808                 }
19809
19810                 for (i in this.ids[sGroup]) {
19811                     var oDD = this.ids[sGroup][i];
19812                     if (! this.isTypeOfDD(oDD)) {
19813                         continue;
19814                     }
19815
19816                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19817                         if (this.isOverTarget(pt, oDD, this.mode)) {
19818                             // look for drop interactions
19819                             if (isDrop) {
19820                                 dropEvts.push( oDD );
19821                             // look for drag enter and drag over interactions
19822                             } else {
19823
19824                                 // initial drag over: dragEnter fires
19825                                 if (!oldOvers[oDD.id]) {
19826                                     enterEvts.push( oDD );
19827                                 // subsequent drag overs: dragOver fires
19828                                 } else {
19829                                     overEvts.push( oDD );
19830                                 }
19831
19832                                 this.dragOvers[oDD.id] = oDD;
19833                             }
19834                         }
19835                     }
19836                 }
19837             }
19838
19839             if (this.mode) {
19840                 if (outEvts.length) {
19841                     dc.b4DragOut(e, outEvts);
19842                     dc.onDragOut(e, outEvts);
19843                 }
19844
19845                 if (enterEvts.length) {
19846                     dc.onDragEnter(e, enterEvts);
19847                 }
19848
19849                 if (overEvts.length) {
19850                     dc.b4DragOver(e, overEvts);
19851                     dc.onDragOver(e, overEvts);
19852                 }
19853
19854                 if (dropEvts.length) {
19855                     dc.b4DragDrop(e, dropEvts);
19856                     dc.onDragDrop(e, dropEvts);
19857                 }
19858
19859             } else {
19860                 // fire dragout events
19861                 var len = 0;
19862                 for (i=0, len=outEvts.length; i<len; ++i) {
19863                     dc.b4DragOut(e, outEvts[i].id);
19864                     dc.onDragOut(e, outEvts[i].id);
19865                 }
19866
19867                 // fire enter events
19868                 for (i=0,len=enterEvts.length; i<len; ++i) {
19869                     // dc.b4DragEnter(e, oDD.id);
19870                     dc.onDragEnter(e, enterEvts[i].id);
19871                 }
19872
19873                 // fire over events
19874                 for (i=0,len=overEvts.length; i<len; ++i) {
19875                     dc.b4DragOver(e, overEvts[i].id);
19876                     dc.onDragOver(e, overEvts[i].id);
19877                 }
19878
19879                 // fire drop events
19880                 for (i=0, len=dropEvts.length; i<len; ++i) {
19881                     dc.b4DragDrop(e, dropEvts[i].id);
19882                     dc.onDragDrop(e, dropEvts[i].id);
19883                 }
19884
19885             }
19886
19887             // notify about a drop that did not find a target
19888             if (isDrop && !dropEvts.length) {
19889                 dc.onInvalidDrop(e);
19890             }
19891
19892         },
19893
19894         /**
19895          * Helper function for getting the best match from the list of drag
19896          * and drop objects returned by the drag and drop events when we are
19897          * in INTERSECT mode.  It returns either the first object that the
19898          * cursor is over, or the object that has the greatest overlap with
19899          * the dragged element.
19900          * @method getBestMatch
19901          * @param  {DragDrop[]} dds The array of drag and drop objects
19902          * targeted
19903          * @return {DragDrop}       The best single match
19904          * @static
19905          */
19906         getBestMatch: function(dds) {
19907             var winner = null;
19908             // Return null if the input is not what we expect
19909             //if (!dds || !dds.length || dds.length == 0) {
19910                // winner = null;
19911             // If there is only one item, it wins
19912             //} else if (dds.length == 1) {
19913
19914             var len = dds.length;
19915
19916             if (len == 1) {
19917                 winner = dds[0];
19918             } else {
19919                 // Loop through the targeted items
19920                 for (var i=0; i<len; ++i) {
19921                     var dd = dds[i];
19922                     // If the cursor is over the object, it wins.  If the
19923                     // cursor is over multiple matches, the first one we come
19924                     // to wins.
19925                     if (dd.cursorIsOver) {
19926                         winner = dd;
19927                         break;
19928                     // Otherwise the object with the most overlap wins
19929                     } else {
19930                         if (!winner ||
19931                             winner.overlap.getArea() < dd.overlap.getArea()) {
19932                             winner = dd;
19933                         }
19934                     }
19935                 }
19936             }
19937
19938             return winner;
19939         },
19940
19941         /**
19942          * Refreshes the cache of the top-left and bottom-right points of the
19943          * drag and drop objects in the specified group(s).  This is in the
19944          * format that is stored in the drag and drop instance, so typical
19945          * usage is:
19946          * <code>
19947          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19948          * </code>
19949          * Alternatively:
19950          * <code>
19951          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19952          * </code>
19953          * @TODO this really should be an indexed array.  Alternatively this
19954          * method could accept both.
19955          * @method refreshCache
19956          * @param {Object} groups an associative array of groups to refresh
19957          * @static
19958          */
19959         refreshCache: function(groups) {
19960             for (var sGroup in groups) {
19961                 if ("string" != typeof sGroup) {
19962                     continue;
19963                 }
19964                 for (var i in this.ids[sGroup]) {
19965                     var oDD = this.ids[sGroup][i];
19966
19967                     if (this.isTypeOfDD(oDD)) {
19968                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
19969                         var loc = this.getLocation(oDD);
19970                         if (loc) {
19971                             this.locationCache[oDD.id] = loc;
19972                         } else {
19973                             delete this.locationCache[oDD.id];
19974                             // this will unregister the drag and drop object if
19975                             // the element is not in a usable state
19976                             // oDD.unreg();
19977                         }
19978                     }
19979                 }
19980             }
19981         },
19982
19983         /**
19984          * This checks to make sure an element exists and is in the DOM.  The
19985          * main purpose is to handle cases where innerHTML is used to remove
19986          * drag and drop objects from the DOM.  IE provides an 'unspecified
19987          * error' when trying to access the offsetParent of such an element
19988          * @method verifyEl
19989          * @param {HTMLElement} el the element to check
19990          * @return {boolean} true if the element looks usable
19991          * @static
19992          */
19993         verifyEl: function(el) {
19994             if (el) {
19995                 var parent;
19996                 if(Roo.isIE){
19997                     try{
19998                         parent = el.offsetParent;
19999                     }catch(e){}
20000                 }else{
20001                     parent = el.offsetParent;
20002                 }
20003                 if (parent) {
20004                     return true;
20005                 }
20006             }
20007
20008             return false;
20009         },
20010
20011         /**
20012          * Returns a Region object containing the drag and drop element's position
20013          * and size, including the padding configured for it
20014          * @method getLocation
20015          * @param {DragDrop} oDD the drag and drop object to get the
20016          *                       location for
20017          * @return {Roo.lib.Region} a Region object representing the total area
20018          *                             the element occupies, including any padding
20019          *                             the instance is configured for.
20020          * @static
20021          */
20022         getLocation: function(oDD) {
20023             if (! this.isTypeOfDD(oDD)) {
20024                 return null;
20025             }
20026
20027             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20028
20029             try {
20030                 pos= Roo.lib.Dom.getXY(el);
20031             } catch (e) { }
20032
20033             if (!pos) {
20034                 return null;
20035             }
20036
20037             x1 = pos[0];
20038             x2 = x1 + el.offsetWidth;
20039             y1 = pos[1];
20040             y2 = y1 + el.offsetHeight;
20041
20042             t = y1 - oDD.padding[0];
20043             r = x2 + oDD.padding[1];
20044             b = y2 + oDD.padding[2];
20045             l = x1 - oDD.padding[3];
20046
20047             return new Roo.lib.Region( t, r, b, l );
20048         },
20049
20050         /**
20051          * Checks the cursor location to see if it over the target
20052          * @method isOverTarget
20053          * @param {Roo.lib.Point} pt The point to evaluate
20054          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20055          * @return {boolean} true if the mouse is over the target
20056          * @private
20057          * @static
20058          */
20059         isOverTarget: function(pt, oTarget, intersect) {
20060             // use cache if available
20061             var loc = this.locationCache[oTarget.id];
20062             if (!loc || !this.useCache) {
20063                 loc = this.getLocation(oTarget);
20064                 this.locationCache[oTarget.id] = loc;
20065
20066             }
20067
20068             if (!loc) {
20069                 return false;
20070             }
20071
20072             oTarget.cursorIsOver = loc.contains( pt );
20073
20074             // DragDrop is using this as a sanity check for the initial mousedown
20075             // in this case we are done.  In POINT mode, if the drag obj has no
20076             // contraints, we are also done. Otherwise we need to evaluate the
20077             // location of the target as related to the actual location of the
20078             // dragged element.
20079             var dc = this.dragCurrent;
20080             if (!dc || !dc.getTargetCoord ||
20081                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20082                 return oTarget.cursorIsOver;
20083             }
20084
20085             oTarget.overlap = null;
20086
20087             // Get the current location of the drag element, this is the
20088             // location of the mouse event less the delta that represents
20089             // where the original mousedown happened on the element.  We
20090             // need to consider constraints and ticks as well.
20091             var pos = dc.getTargetCoord(pt.x, pt.y);
20092
20093             var el = dc.getDragEl();
20094             var curRegion = new Roo.lib.Region( pos.y,
20095                                                    pos.x + el.offsetWidth,
20096                                                    pos.y + el.offsetHeight,
20097                                                    pos.x );
20098
20099             var overlap = curRegion.intersect(loc);
20100
20101             if (overlap) {
20102                 oTarget.overlap = overlap;
20103                 return (intersect) ? true : oTarget.cursorIsOver;
20104             } else {
20105                 return false;
20106             }
20107         },
20108
20109         /**
20110          * unload event handler
20111          * @method _onUnload
20112          * @private
20113          * @static
20114          */
20115         _onUnload: function(e, me) {
20116             Roo.dd.DragDropMgr.unregAll();
20117         },
20118
20119         /**
20120          * Cleans up the drag and drop events and objects.
20121          * @method unregAll
20122          * @private
20123          * @static
20124          */
20125         unregAll: function() {
20126
20127             if (this.dragCurrent) {
20128                 this.stopDrag();
20129                 this.dragCurrent = null;
20130             }
20131
20132             this._execOnAll("unreg", []);
20133
20134             for (i in this.elementCache) {
20135                 delete this.elementCache[i];
20136             }
20137
20138             this.elementCache = {};
20139             this.ids = {};
20140         },
20141
20142         /**
20143          * A cache of DOM elements
20144          * @property elementCache
20145          * @private
20146          * @static
20147          */
20148         elementCache: {},
20149
20150         /**
20151          * Get the wrapper for the DOM element specified
20152          * @method getElWrapper
20153          * @param {String} id the id of the element to get
20154          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20155          * @private
20156          * @deprecated This wrapper isn't that useful
20157          * @static
20158          */
20159         getElWrapper: function(id) {
20160             var oWrapper = this.elementCache[id];
20161             if (!oWrapper || !oWrapper.el) {
20162                 oWrapper = this.elementCache[id] =
20163                     new this.ElementWrapper(Roo.getDom(id));
20164             }
20165             return oWrapper;
20166         },
20167
20168         /**
20169          * Returns the actual DOM element
20170          * @method getElement
20171          * @param {String} id the id of the elment to get
20172          * @return {Object} The element
20173          * @deprecated use Roo.getDom instead
20174          * @static
20175          */
20176         getElement: function(id) {
20177             return Roo.getDom(id);
20178         },
20179
20180         /**
20181          * Returns the style property for the DOM element (i.e.,
20182          * document.getElById(id).style)
20183          * @method getCss
20184          * @param {String} id the id of the elment to get
20185          * @return {Object} The style property of the element
20186          * @deprecated use Roo.getDom instead
20187          * @static
20188          */
20189         getCss: function(id) {
20190             var el = Roo.getDom(id);
20191             return (el) ? el.style : null;
20192         },
20193
20194         /**
20195          * Inner class for cached elements
20196          * @class DragDropMgr.ElementWrapper
20197          * @for DragDropMgr
20198          * @private
20199          * @deprecated
20200          */
20201         ElementWrapper: function(el) {
20202                 /**
20203                  * The element
20204                  * @property el
20205                  */
20206                 this.el = el || null;
20207                 /**
20208                  * The element id
20209                  * @property id
20210                  */
20211                 this.id = this.el && el.id;
20212                 /**
20213                  * A reference to the style property
20214                  * @property css
20215                  */
20216                 this.css = this.el && el.style;
20217             },
20218
20219         /**
20220          * Returns the X position of an html element
20221          * @method getPosX
20222          * @param el the element for which to get the position
20223          * @return {int} the X coordinate
20224          * @for DragDropMgr
20225          * @deprecated use Roo.lib.Dom.getX instead
20226          * @static
20227          */
20228         getPosX: function(el) {
20229             return Roo.lib.Dom.getX(el);
20230         },
20231
20232         /**
20233          * Returns the Y position of an html element
20234          * @method getPosY
20235          * @param el the element for which to get the position
20236          * @return {int} the Y coordinate
20237          * @deprecated use Roo.lib.Dom.getY instead
20238          * @static
20239          */
20240         getPosY: function(el) {
20241             return Roo.lib.Dom.getY(el);
20242         },
20243
20244         /**
20245          * Swap two nodes.  In IE, we use the native method, for others we
20246          * emulate the IE behavior
20247          * @method swapNode
20248          * @param n1 the first node to swap
20249          * @param n2 the other node to swap
20250          * @static
20251          */
20252         swapNode: function(n1, n2) {
20253             if (n1.swapNode) {
20254                 n1.swapNode(n2);
20255             } else {
20256                 var p = n2.parentNode;
20257                 var s = n2.nextSibling;
20258
20259                 if (s == n1) {
20260                     p.insertBefore(n1, n2);
20261                 } else if (n2 == n1.nextSibling) {
20262                     p.insertBefore(n2, n1);
20263                 } else {
20264                     n1.parentNode.replaceChild(n2, n1);
20265                     p.insertBefore(n1, s);
20266                 }
20267             }
20268         },
20269
20270         /**
20271          * Returns the current scroll position
20272          * @method getScroll
20273          * @private
20274          * @static
20275          */
20276         getScroll: function () {
20277             var t, l, dde=document.documentElement, db=document.body;
20278             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20279                 t = dde.scrollTop;
20280                 l = dde.scrollLeft;
20281             } else if (db) {
20282                 t = db.scrollTop;
20283                 l = db.scrollLeft;
20284             } else {
20285
20286             }
20287             return { top: t, left: l };
20288         },
20289
20290         /**
20291          * Returns the specified element style property
20292          * @method getStyle
20293          * @param {HTMLElement} el          the element
20294          * @param {string}      styleProp   the style property
20295          * @return {string} The value of the style property
20296          * @deprecated use Roo.lib.Dom.getStyle
20297          * @static
20298          */
20299         getStyle: function(el, styleProp) {
20300             return Roo.fly(el).getStyle(styleProp);
20301         },
20302
20303         /**
20304          * Gets the scrollTop
20305          * @method getScrollTop
20306          * @return {int} the document's scrollTop
20307          * @static
20308          */
20309         getScrollTop: function () { return this.getScroll().top; },
20310
20311         /**
20312          * Gets the scrollLeft
20313          * @method getScrollLeft
20314          * @return {int} the document's scrollTop
20315          * @static
20316          */
20317         getScrollLeft: function () { return this.getScroll().left; },
20318
20319         /**
20320          * Sets the x/y position of an element to the location of the
20321          * target element.
20322          * @method moveToEl
20323          * @param {HTMLElement} moveEl      The element to move
20324          * @param {HTMLElement} targetEl    The position reference element
20325          * @static
20326          */
20327         moveToEl: function (moveEl, targetEl) {
20328             var aCoord = Roo.lib.Dom.getXY(targetEl);
20329             Roo.lib.Dom.setXY(moveEl, aCoord);
20330         },
20331
20332         /**
20333          * Numeric array sort function
20334          * @method numericSort
20335          * @static
20336          */
20337         numericSort: function(a, b) { return (a - b); },
20338
20339         /**
20340          * Internal counter
20341          * @property _timeoutCount
20342          * @private
20343          * @static
20344          */
20345         _timeoutCount: 0,
20346
20347         /**
20348          * Trying to make the load order less important.  Without this we get
20349          * an error if this file is loaded before the Event Utility.
20350          * @method _addListeners
20351          * @private
20352          * @static
20353          */
20354         _addListeners: function() {
20355             var DDM = Roo.dd.DDM;
20356             if ( Roo.lib.Event && document ) {
20357                 DDM._onLoad();
20358             } else {
20359                 if (DDM._timeoutCount > 2000) {
20360                 } else {
20361                     setTimeout(DDM._addListeners, 10);
20362                     if (document && document.body) {
20363                         DDM._timeoutCount += 1;
20364                     }
20365                 }
20366             }
20367         },
20368
20369         /**
20370          * Recursively searches the immediate parent and all child nodes for
20371          * the handle element in order to determine wheter or not it was
20372          * clicked.
20373          * @method handleWasClicked
20374          * @param node the html element to inspect
20375          * @static
20376          */
20377         handleWasClicked: function(node, id) {
20378             if (this.isHandle(id, node.id)) {
20379                 return true;
20380             } else {
20381                 // check to see if this is a text node child of the one we want
20382                 var p = node.parentNode;
20383
20384                 while (p) {
20385                     if (this.isHandle(id, p.id)) {
20386                         return true;
20387                     } else {
20388                         p = p.parentNode;
20389                     }
20390                 }
20391             }
20392
20393             return false;
20394         }
20395
20396     };
20397
20398 }();
20399
20400 // shorter alias, save a few bytes
20401 Roo.dd.DDM = Roo.dd.DragDropMgr;
20402 Roo.dd.DDM._addListeners();
20403
20404 }/*
20405  * Based on:
20406  * Ext JS Library 1.1.1
20407  * Copyright(c) 2006-2007, Ext JS, LLC.
20408  *
20409  * Originally Released Under LGPL - original licence link has changed is not relivant.
20410  *
20411  * Fork - LGPL
20412  * <script type="text/javascript">
20413  */
20414
20415 /**
20416  * @class Roo.dd.DD
20417  * A DragDrop implementation where the linked element follows the
20418  * mouse cursor during a drag.
20419  * @extends Roo.dd.DragDrop
20420  * @constructor
20421  * @param {String} id the id of the linked element
20422  * @param {String} sGroup the group of related DragDrop items
20423  * @param {object} config an object containing configurable attributes
20424  *                Valid properties for DD:
20425  *                    scroll
20426  */
20427 Roo.dd.DD = function(id, sGroup, config) {
20428     if (id) {
20429         this.init(id, sGroup, config);
20430     }
20431 };
20432
20433 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20434
20435     /**
20436      * When set to true, the utility automatically tries to scroll the browser
20437      * window wehn a drag and drop element is dragged near the viewport boundary.
20438      * Defaults to true.
20439      * @property scroll
20440      * @type boolean
20441      */
20442     scroll: true,
20443
20444     /**
20445      * Sets the pointer offset to the distance between the linked element's top
20446      * left corner and the location the element was clicked
20447      * @method autoOffset
20448      * @param {int} iPageX the X coordinate of the click
20449      * @param {int} iPageY the Y coordinate of the click
20450      */
20451     autoOffset: function(iPageX, iPageY) {
20452         var x = iPageX - this.startPageX;
20453         var y = iPageY - this.startPageY;
20454         this.setDelta(x, y);
20455     },
20456
20457     /**
20458      * Sets the pointer offset.  You can call this directly to force the
20459      * offset to be in a particular location (e.g., pass in 0,0 to set it
20460      * to the center of the object)
20461      * @method setDelta
20462      * @param {int} iDeltaX the distance from the left
20463      * @param {int} iDeltaY the distance from the top
20464      */
20465     setDelta: function(iDeltaX, iDeltaY) {
20466         this.deltaX = iDeltaX;
20467         this.deltaY = iDeltaY;
20468     },
20469
20470     /**
20471      * Sets the drag element to the location of the mousedown or click event,
20472      * maintaining the cursor location relative to the location on the element
20473      * that was clicked.  Override this if you want to place the element in a
20474      * location other than where the cursor is.
20475      * @method setDragElPos
20476      * @param {int} iPageX the X coordinate of the mousedown or drag event
20477      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20478      */
20479     setDragElPos: function(iPageX, iPageY) {
20480         // the first time we do this, we are going to check to make sure
20481         // the element has css positioning
20482
20483         var el = this.getDragEl();
20484         this.alignElWithMouse(el, iPageX, iPageY);
20485     },
20486
20487     /**
20488      * Sets the element to the location of the mousedown or click event,
20489      * maintaining the cursor location relative to the location on the element
20490      * that was clicked.  Override this if you want to place the element in a
20491      * location other than where the cursor is.
20492      * @method alignElWithMouse
20493      * @param {HTMLElement} el the element to move
20494      * @param {int} iPageX the X coordinate of the mousedown or drag event
20495      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20496      */
20497     alignElWithMouse: function(el, iPageX, iPageY) {
20498         var oCoord = this.getTargetCoord(iPageX, iPageY);
20499         var fly = el.dom ? el : Roo.fly(el);
20500         if (!this.deltaSetXY) {
20501             var aCoord = [oCoord.x, oCoord.y];
20502             fly.setXY(aCoord);
20503             var newLeft = fly.getLeft(true);
20504             var newTop  = fly.getTop(true);
20505             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20506         } else {
20507             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20508         }
20509
20510         this.cachePosition(oCoord.x, oCoord.y);
20511         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20512         return oCoord;
20513     },
20514
20515     /**
20516      * Saves the most recent position so that we can reset the constraints and
20517      * tick marks on-demand.  We need to know this so that we can calculate the
20518      * number of pixels the element is offset from its original position.
20519      * @method cachePosition
20520      * @param iPageX the current x position (optional, this just makes it so we
20521      * don't have to look it up again)
20522      * @param iPageY the current y position (optional, this just makes it so we
20523      * don't have to look it up again)
20524      */
20525     cachePosition: function(iPageX, iPageY) {
20526         if (iPageX) {
20527             this.lastPageX = iPageX;
20528             this.lastPageY = iPageY;
20529         } else {
20530             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20531             this.lastPageX = aCoord[0];
20532             this.lastPageY = aCoord[1];
20533         }
20534     },
20535
20536     /**
20537      * Auto-scroll the window if the dragged object has been moved beyond the
20538      * visible window boundary.
20539      * @method autoScroll
20540      * @param {int} x the drag element's x position
20541      * @param {int} y the drag element's y position
20542      * @param {int} h the height of the drag element
20543      * @param {int} w the width of the drag element
20544      * @private
20545      */
20546     autoScroll: function(x, y, h, w) {
20547
20548         if (this.scroll) {
20549             // The client height
20550             var clientH = Roo.lib.Dom.getViewWidth();
20551
20552             // The client width
20553             var clientW = Roo.lib.Dom.getViewHeight();
20554
20555             // The amt scrolled down
20556             var st = this.DDM.getScrollTop();
20557
20558             // The amt scrolled right
20559             var sl = this.DDM.getScrollLeft();
20560
20561             // Location of the bottom of the element
20562             var bot = h + y;
20563
20564             // Location of the right of the element
20565             var right = w + x;
20566
20567             // The distance from the cursor to the bottom of the visible area,
20568             // adjusted so that we don't scroll if the cursor is beyond the
20569             // element drag constraints
20570             var toBot = (clientH + st - y - this.deltaY);
20571
20572             // The distance from the cursor to the right of the visible area
20573             var toRight = (clientW + sl - x - this.deltaX);
20574
20575
20576             // How close to the edge the cursor must be before we scroll
20577             // var thresh = (document.all) ? 100 : 40;
20578             var thresh = 40;
20579
20580             // How many pixels to scroll per autoscroll op.  This helps to reduce
20581             // clunky scrolling. IE is more sensitive about this ... it needs this
20582             // value to be higher.
20583             var scrAmt = (document.all) ? 80 : 30;
20584
20585             // Scroll down if we are near the bottom of the visible page and the
20586             // obj extends below the crease
20587             if ( bot > clientH && toBot < thresh ) {
20588                 window.scrollTo(sl, st + scrAmt);
20589             }
20590
20591             // Scroll up if the window is scrolled down and the top of the object
20592             // goes above the top border
20593             if ( y < st && st > 0 && y - st < thresh ) {
20594                 window.scrollTo(sl, st - scrAmt);
20595             }
20596
20597             // Scroll right if the obj is beyond the right border and the cursor is
20598             // near the border.
20599             if ( right > clientW && toRight < thresh ) {
20600                 window.scrollTo(sl + scrAmt, st);
20601             }
20602
20603             // Scroll left if the window has been scrolled to the right and the obj
20604             // extends past the left border
20605             if ( x < sl && sl > 0 && x - sl < thresh ) {
20606                 window.scrollTo(sl - scrAmt, st);
20607             }
20608         }
20609     },
20610
20611     /**
20612      * Finds the location the element should be placed if we want to move
20613      * it to where the mouse location less the click offset would place us.
20614      * @method getTargetCoord
20615      * @param {int} iPageX the X coordinate of the click
20616      * @param {int} iPageY the Y coordinate of the click
20617      * @return an object that contains the coordinates (Object.x and Object.y)
20618      * @private
20619      */
20620     getTargetCoord: function(iPageX, iPageY) {
20621
20622
20623         var x = iPageX - this.deltaX;
20624         var y = iPageY - this.deltaY;
20625
20626         if (this.constrainX) {
20627             if (x < this.minX) { x = this.minX; }
20628             if (x > this.maxX) { x = this.maxX; }
20629         }
20630
20631         if (this.constrainY) {
20632             if (y < this.minY) { y = this.minY; }
20633             if (y > this.maxY) { y = this.maxY; }
20634         }
20635
20636         x = this.getTick(x, this.xTicks);
20637         y = this.getTick(y, this.yTicks);
20638
20639
20640         return {x:x, y:y};
20641     },
20642
20643     /*
20644      * Sets up config options specific to this class. Overrides
20645      * Roo.dd.DragDrop, but all versions of this method through the
20646      * inheritance chain are called
20647      */
20648     applyConfig: function() {
20649         Roo.dd.DD.superclass.applyConfig.call(this);
20650         this.scroll = (this.config.scroll !== false);
20651     },
20652
20653     /*
20654      * Event that fires prior to the onMouseDown event.  Overrides
20655      * Roo.dd.DragDrop.
20656      */
20657     b4MouseDown: function(e) {
20658         // this.resetConstraints();
20659         this.autoOffset(e.getPageX(),
20660                             e.getPageY());
20661     },
20662
20663     /*
20664      * Event that fires prior to the onDrag event.  Overrides
20665      * Roo.dd.DragDrop.
20666      */
20667     b4Drag: function(e) {
20668         this.setDragElPos(e.getPageX(),
20669                             e.getPageY());
20670     },
20671
20672     toString: function() {
20673         return ("DD " + this.id);
20674     }
20675
20676     //////////////////////////////////////////////////////////////////////////
20677     // Debugging ygDragDrop events that can be overridden
20678     //////////////////////////////////////////////////////////////////////////
20679     /*
20680     startDrag: function(x, y) {
20681     },
20682
20683     onDrag: function(e) {
20684     },
20685
20686     onDragEnter: function(e, id) {
20687     },
20688
20689     onDragOver: function(e, id) {
20690     },
20691
20692     onDragOut: function(e, id) {
20693     },
20694
20695     onDragDrop: function(e, id) {
20696     },
20697
20698     endDrag: function(e) {
20699     }
20700
20701     */
20702
20703 });/*
20704  * Based on:
20705  * Ext JS Library 1.1.1
20706  * Copyright(c) 2006-2007, Ext JS, LLC.
20707  *
20708  * Originally Released Under LGPL - original licence link has changed is not relivant.
20709  *
20710  * Fork - LGPL
20711  * <script type="text/javascript">
20712  */
20713
20714 /**
20715  * @class Roo.dd.DDProxy
20716  * A DragDrop implementation that inserts an empty, bordered div into
20717  * the document that follows the cursor during drag operations.  At the time of
20718  * the click, the frame div is resized to the dimensions of the linked html
20719  * element, and moved to the exact location of the linked element.
20720  *
20721  * References to the "frame" element refer to the single proxy element that
20722  * was created to be dragged in place of all DDProxy elements on the
20723  * page.
20724  *
20725  * @extends Roo.dd.DD
20726  * @constructor
20727  * @param {String} id the id of the linked html element
20728  * @param {String} sGroup the group of related DragDrop objects
20729  * @param {object} config an object containing configurable attributes
20730  *                Valid properties for DDProxy in addition to those in DragDrop:
20731  *                   resizeFrame, centerFrame, dragElId
20732  */
20733 Roo.dd.DDProxy = function(id, sGroup, config) {
20734     if (id) {
20735         this.init(id, sGroup, config);
20736         this.initFrame();
20737     }
20738 };
20739
20740 /**
20741  * The default drag frame div id
20742  * @property Roo.dd.DDProxy.dragElId
20743  * @type String
20744  * @static
20745  */
20746 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20747
20748 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20749
20750     /**
20751      * By default we resize the drag frame to be the same size as the element
20752      * we want to drag (this is to get the frame effect).  We can turn it off
20753      * if we want a different behavior.
20754      * @property resizeFrame
20755      * @type boolean
20756      */
20757     resizeFrame: true,
20758
20759     /**
20760      * By default the frame is positioned exactly where the drag element is, so
20761      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20762      * you do not have constraints on the obj is to have the drag frame centered
20763      * around the cursor.  Set centerFrame to true for this effect.
20764      * @property centerFrame
20765      * @type boolean
20766      */
20767     centerFrame: false,
20768
20769     /**
20770      * Creates the proxy element if it does not yet exist
20771      * @method createFrame
20772      */
20773     createFrame: function() {
20774         var self = this;
20775         var body = document.body;
20776
20777         if (!body || !body.firstChild) {
20778             setTimeout( function() { self.createFrame(); }, 50 );
20779             return;
20780         }
20781
20782         var div = this.getDragEl();
20783
20784         if (!div) {
20785             div    = document.createElement("div");
20786             div.id = this.dragElId;
20787             var s  = div.style;
20788
20789             s.position   = "absolute";
20790             s.visibility = "hidden";
20791             s.cursor     = "move";
20792             s.border     = "2px solid #aaa";
20793             s.zIndex     = 999;
20794
20795             // appendChild can blow up IE if invoked prior to the window load event
20796             // while rendering a table.  It is possible there are other scenarios
20797             // that would cause this to happen as well.
20798             body.insertBefore(div, body.firstChild);
20799         }
20800     },
20801
20802     /**
20803      * Initialization for the drag frame element.  Must be called in the
20804      * constructor of all subclasses
20805      * @method initFrame
20806      */
20807     initFrame: function() {
20808         this.createFrame();
20809     },
20810
20811     applyConfig: function() {
20812         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20813
20814         this.resizeFrame = (this.config.resizeFrame !== false);
20815         this.centerFrame = (this.config.centerFrame);
20816         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20817     },
20818
20819     /**
20820      * Resizes the drag frame to the dimensions of the clicked object, positions
20821      * it over the object, and finally displays it
20822      * @method showFrame
20823      * @param {int} iPageX X click position
20824      * @param {int} iPageY Y click position
20825      * @private
20826      */
20827     showFrame: function(iPageX, iPageY) {
20828         var el = this.getEl();
20829         var dragEl = this.getDragEl();
20830         var s = dragEl.style;
20831
20832         this._resizeProxy();
20833
20834         if (this.centerFrame) {
20835             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20836                            Math.round(parseInt(s.height, 10)/2) );
20837         }
20838
20839         this.setDragElPos(iPageX, iPageY);
20840
20841         Roo.fly(dragEl).show();
20842     },
20843
20844     /**
20845      * The proxy is automatically resized to the dimensions of the linked
20846      * element when a drag is initiated, unless resizeFrame is set to false
20847      * @method _resizeProxy
20848      * @private
20849      */
20850     _resizeProxy: function() {
20851         if (this.resizeFrame) {
20852             var el = this.getEl();
20853             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20854         }
20855     },
20856
20857     // overrides Roo.dd.DragDrop
20858     b4MouseDown: function(e) {
20859         var x = e.getPageX();
20860         var y = e.getPageY();
20861         this.autoOffset(x, y);
20862         this.setDragElPos(x, y);
20863     },
20864
20865     // overrides Roo.dd.DragDrop
20866     b4StartDrag: function(x, y) {
20867         // show the drag frame
20868         this.showFrame(x, y);
20869     },
20870
20871     // overrides Roo.dd.DragDrop
20872     b4EndDrag: function(e) {
20873         Roo.fly(this.getDragEl()).hide();
20874     },
20875
20876     // overrides Roo.dd.DragDrop
20877     // By default we try to move the element to the last location of the frame.
20878     // This is so that the default behavior mirrors that of Roo.dd.DD.
20879     endDrag: function(e) {
20880
20881         var lel = this.getEl();
20882         var del = this.getDragEl();
20883
20884         // Show the drag frame briefly so we can get its position
20885         del.style.visibility = "";
20886
20887         this.beforeMove();
20888         // Hide the linked element before the move to get around a Safari
20889         // rendering bug.
20890         lel.style.visibility = "hidden";
20891         Roo.dd.DDM.moveToEl(lel, del);
20892         del.style.visibility = "hidden";
20893         lel.style.visibility = "";
20894
20895         this.afterDrag();
20896     },
20897
20898     beforeMove : function(){
20899
20900     },
20901
20902     afterDrag : function(){
20903
20904     },
20905
20906     toString: function() {
20907         return ("DDProxy " + this.id);
20908     }
20909
20910 });
20911 /*
20912  * Based on:
20913  * Ext JS Library 1.1.1
20914  * Copyright(c) 2006-2007, Ext JS, LLC.
20915  *
20916  * Originally Released Under LGPL - original licence link has changed is not relivant.
20917  *
20918  * Fork - LGPL
20919  * <script type="text/javascript">
20920  */
20921
20922  /**
20923  * @class Roo.dd.DDTarget
20924  * A DragDrop implementation that does not move, but can be a drop
20925  * target.  You would get the same result by simply omitting implementation
20926  * for the event callbacks, but this way we reduce the processing cost of the
20927  * event listener and the callbacks.
20928  * @extends Roo.dd.DragDrop
20929  * @constructor
20930  * @param {String} id the id of the element that is a drop target
20931  * @param {String} sGroup the group of related DragDrop objects
20932  * @param {object} config an object containing configurable attributes
20933  *                 Valid properties for DDTarget in addition to those in
20934  *                 DragDrop:
20935  *                    none
20936  */
20937 Roo.dd.DDTarget = function(id, sGroup, config) {
20938     if (id) {
20939         this.initTarget(id, sGroup, config);
20940     }
20941     if (config.listeners || config.events) { 
20942        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20943             listeners : config.listeners || {}, 
20944             events : config.events || {} 
20945         });    
20946     }
20947 };
20948
20949 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20950 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20951     toString: function() {
20952         return ("DDTarget " + this.id);
20953     }
20954 });
20955 /*
20956  * Based on:
20957  * Ext JS Library 1.1.1
20958  * Copyright(c) 2006-2007, Ext JS, LLC.
20959  *
20960  * Originally Released Under LGPL - original licence link has changed is not relivant.
20961  *
20962  * Fork - LGPL
20963  * <script type="text/javascript">
20964  */
20965  
20966
20967 /**
20968  * @class Roo.dd.ScrollManager
20969  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
20970  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
20971  * @singleton
20972  */
20973 Roo.dd.ScrollManager = function(){
20974     var ddm = Roo.dd.DragDropMgr;
20975     var els = {};
20976     var dragEl = null;
20977     var proc = {};
20978     
20979     
20980     
20981     var onStop = function(e){
20982         dragEl = null;
20983         clearProc();
20984     };
20985     
20986     var triggerRefresh = function(){
20987         if(ddm.dragCurrent){
20988              ddm.refreshCache(ddm.dragCurrent.groups);
20989         }
20990     };
20991     
20992     var doScroll = function(){
20993         if(ddm.dragCurrent){
20994             var dds = Roo.dd.ScrollManager;
20995             if(!dds.animate){
20996                 if(proc.el.scroll(proc.dir, dds.increment)){
20997                     triggerRefresh();
20998                 }
20999             }else{
21000                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21001             }
21002         }
21003     };
21004     
21005     var clearProc = function(){
21006         if(proc.id){
21007             clearInterval(proc.id);
21008         }
21009         proc.id = 0;
21010         proc.el = null;
21011         proc.dir = "";
21012     };
21013     
21014     var startProc = function(el, dir){
21015          Roo.log('scroll startproc');
21016         clearProc();
21017         proc.el = el;
21018         proc.dir = dir;
21019         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21020     };
21021     
21022     var onFire = function(e, isDrop){
21023        
21024         if(isDrop || !ddm.dragCurrent){ return; }
21025         var dds = Roo.dd.ScrollManager;
21026         if(!dragEl || dragEl != ddm.dragCurrent){
21027             dragEl = ddm.dragCurrent;
21028             // refresh regions on drag start
21029             dds.refreshCache();
21030         }
21031         
21032         var xy = Roo.lib.Event.getXY(e);
21033         var pt = new Roo.lib.Point(xy[0], xy[1]);
21034         for(var id in els){
21035             var el = els[id], r = el._region;
21036             if(r && r.contains(pt) && el.isScrollable()){
21037                 if(r.bottom - pt.y <= dds.thresh){
21038                     if(proc.el != el){
21039                         startProc(el, "down");
21040                     }
21041                     return;
21042                 }else if(r.right - pt.x <= dds.thresh){
21043                     if(proc.el != el){
21044                         startProc(el, "left");
21045                     }
21046                     return;
21047                 }else if(pt.y - r.top <= dds.thresh){
21048                     if(proc.el != el){
21049                         startProc(el, "up");
21050                     }
21051                     return;
21052                 }else if(pt.x - r.left <= dds.thresh){
21053                     if(proc.el != el){
21054                         startProc(el, "right");
21055                     }
21056                     return;
21057                 }
21058             }
21059         }
21060         clearProc();
21061     };
21062     
21063     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21064     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21065     
21066     return {
21067         /**
21068          * Registers new overflow element(s) to auto scroll
21069          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21070          */
21071         register : function(el){
21072             if(el instanceof Array){
21073                 for(var i = 0, len = el.length; i < len; i++) {
21074                         this.register(el[i]);
21075                 }
21076             }else{
21077                 el = Roo.get(el);
21078                 els[el.id] = el;
21079             }
21080             Roo.dd.ScrollManager.els = els;
21081         },
21082         
21083         /**
21084          * Unregisters overflow element(s) so they are no longer scrolled
21085          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21086          */
21087         unregister : function(el){
21088             if(el instanceof Array){
21089                 for(var i = 0, len = el.length; i < len; i++) {
21090                         this.unregister(el[i]);
21091                 }
21092             }else{
21093                 el = Roo.get(el);
21094                 delete els[el.id];
21095             }
21096         },
21097         
21098         /**
21099          * The number of pixels from the edge of a container the pointer needs to be to 
21100          * trigger scrolling (defaults to 25)
21101          * @type Number
21102          */
21103         thresh : 25,
21104         
21105         /**
21106          * The number of pixels to scroll in each scroll increment (defaults to 50)
21107          * @type Number
21108          */
21109         increment : 100,
21110         
21111         /**
21112          * The frequency of scrolls in milliseconds (defaults to 500)
21113          * @type Number
21114          */
21115         frequency : 500,
21116         
21117         /**
21118          * True to animate the scroll (defaults to true)
21119          * @type Boolean
21120          */
21121         animate: true,
21122         
21123         /**
21124          * The animation duration in seconds - 
21125          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21126          * @type Number
21127          */
21128         animDuration: .4,
21129         
21130         /**
21131          * Manually trigger a cache refresh.
21132          */
21133         refreshCache : function(){
21134             for(var id in els){
21135                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21136                     els[id]._region = els[id].getRegion();
21137                 }
21138             }
21139         }
21140     };
21141 }();/*
21142  * Based on:
21143  * Ext JS Library 1.1.1
21144  * Copyright(c) 2006-2007, Ext JS, LLC.
21145  *
21146  * Originally Released Under LGPL - original licence link has changed is not relivant.
21147  *
21148  * Fork - LGPL
21149  * <script type="text/javascript">
21150  */
21151  
21152
21153 /**
21154  * @class Roo.dd.Registry
21155  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21156  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21157  * @singleton
21158  */
21159 Roo.dd.Registry = function(){
21160     var elements = {}; 
21161     var handles = {}; 
21162     var autoIdSeed = 0;
21163
21164     var getId = function(el, autogen){
21165         if(typeof el == "string"){
21166             return el;
21167         }
21168         var id = el.id;
21169         if(!id && autogen !== false){
21170             id = "roodd-" + (++autoIdSeed);
21171             el.id = id;
21172         }
21173         return id;
21174     };
21175     
21176     return {
21177     /**
21178      * Register a drag drop element
21179      * @param {String|HTMLElement} element The id or DOM node to register
21180      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21181      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21182      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21183      * populated in the data object (if applicable):
21184      * <pre>
21185 Value      Description<br />
21186 ---------  ------------------------------------------<br />
21187 handles    Array of DOM nodes that trigger dragging<br />
21188            for the element being registered<br />
21189 isHandle   True if the element passed in triggers<br />
21190            dragging itself, else false
21191 </pre>
21192      */
21193         register : function(el, data){
21194             data = data || {};
21195             if(typeof el == "string"){
21196                 el = document.getElementById(el);
21197             }
21198             data.ddel = el;
21199             elements[getId(el)] = data;
21200             if(data.isHandle !== false){
21201                 handles[data.ddel.id] = data;
21202             }
21203             if(data.handles){
21204                 var hs = data.handles;
21205                 for(var i = 0, len = hs.length; i < len; i++){
21206                         handles[getId(hs[i])] = data;
21207                 }
21208             }
21209         },
21210
21211     /**
21212      * Unregister a drag drop element
21213      * @param {String|HTMLElement}  element The id or DOM node to unregister
21214      */
21215         unregister : function(el){
21216             var id = getId(el, false);
21217             var data = elements[id];
21218             if(data){
21219                 delete elements[id];
21220                 if(data.handles){
21221                     var hs = data.handles;
21222                     for(var i = 0, len = hs.length; i < len; i++){
21223                         delete handles[getId(hs[i], false)];
21224                     }
21225                 }
21226             }
21227         },
21228
21229     /**
21230      * Returns the handle registered for a DOM Node by id
21231      * @param {String|HTMLElement} id The DOM node or id to look up
21232      * @return {Object} handle The custom handle data
21233      */
21234         getHandle : function(id){
21235             if(typeof id != "string"){ // must be element?
21236                 id = id.id;
21237             }
21238             return handles[id];
21239         },
21240
21241     /**
21242      * Returns the handle that is registered for the DOM node that is the target of the event
21243      * @param {Event} e The event
21244      * @return {Object} handle The custom handle data
21245      */
21246         getHandleFromEvent : function(e){
21247             var t = Roo.lib.Event.getTarget(e);
21248             return t ? handles[t.id] : null;
21249         },
21250
21251     /**
21252      * Returns a custom data object that is registered for a DOM node by id
21253      * @param {String|HTMLElement} id The DOM node or id to look up
21254      * @return {Object} data The custom data
21255      */
21256         getTarget : function(id){
21257             if(typeof id != "string"){ // must be element?
21258                 id = id.id;
21259             }
21260             return elements[id];
21261         },
21262
21263     /**
21264      * Returns a custom data object that is registered for the DOM node that is the target of the event
21265      * @param {Event} e The event
21266      * @return {Object} data The custom data
21267      */
21268         getTargetFromEvent : function(e){
21269             var t = Roo.lib.Event.getTarget(e);
21270             return t ? elements[t.id] || handles[t.id] : null;
21271         }
21272     };
21273 }();/*
21274  * Based on:
21275  * Ext JS Library 1.1.1
21276  * Copyright(c) 2006-2007, Ext JS, LLC.
21277  *
21278  * Originally Released Under LGPL - original licence link has changed is not relivant.
21279  *
21280  * Fork - LGPL
21281  * <script type="text/javascript">
21282  */
21283  
21284
21285 /**
21286  * @class Roo.dd.StatusProxy
21287  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21288  * default drag proxy used by all Roo.dd components.
21289  * @constructor
21290  * @param {Object} config
21291  */
21292 Roo.dd.StatusProxy = function(config){
21293     Roo.apply(this, config);
21294     this.id = this.id || Roo.id();
21295     this.el = new Roo.Layer({
21296         dh: {
21297             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21298                 {tag: "div", cls: "x-dd-drop-icon"},
21299                 {tag: "div", cls: "x-dd-drag-ghost"}
21300             ]
21301         }, 
21302         shadow: !config || config.shadow !== false
21303     });
21304     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21305     this.dropStatus = this.dropNotAllowed;
21306 };
21307
21308 Roo.dd.StatusProxy.prototype = {
21309     /**
21310      * @cfg {String} dropAllowed
21311      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21312      */
21313     dropAllowed : "x-dd-drop-ok",
21314     /**
21315      * @cfg {String} dropNotAllowed
21316      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21317      */
21318     dropNotAllowed : "x-dd-drop-nodrop",
21319
21320     /**
21321      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21322      * over the current target element.
21323      * @param {String} cssClass The css class for the new drop status indicator image
21324      */
21325     setStatus : function(cssClass){
21326         cssClass = cssClass || this.dropNotAllowed;
21327         if(this.dropStatus != cssClass){
21328             this.el.replaceClass(this.dropStatus, cssClass);
21329             this.dropStatus = cssClass;
21330         }
21331     },
21332
21333     /**
21334      * Resets the status indicator to the default dropNotAllowed value
21335      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21336      */
21337     reset : function(clearGhost){
21338         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21339         this.dropStatus = this.dropNotAllowed;
21340         if(clearGhost){
21341             this.ghost.update("");
21342         }
21343     },
21344
21345     /**
21346      * Updates the contents of the ghost element
21347      * @param {String} html The html that will replace the current innerHTML of the ghost element
21348      */
21349     update : function(html){
21350         if(typeof html == "string"){
21351             this.ghost.update(html);
21352         }else{
21353             this.ghost.update("");
21354             html.style.margin = "0";
21355             this.ghost.dom.appendChild(html);
21356         }
21357         // ensure float = none set?? cant remember why though.
21358         var el = this.ghost.dom.firstChild;
21359                 if(el){
21360                         Roo.fly(el).setStyle('float', 'none');
21361                 }
21362     },
21363     
21364     /**
21365      * Returns the underlying proxy {@link Roo.Layer}
21366      * @return {Roo.Layer} el
21367     */
21368     getEl : function(){
21369         return this.el;
21370     },
21371
21372     /**
21373      * Returns the ghost element
21374      * @return {Roo.Element} el
21375      */
21376     getGhost : function(){
21377         return this.ghost;
21378     },
21379
21380     /**
21381      * Hides the proxy
21382      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21383      */
21384     hide : function(clear){
21385         this.el.hide();
21386         if(clear){
21387             this.reset(true);
21388         }
21389     },
21390
21391     /**
21392      * Stops the repair animation if it's currently running
21393      */
21394     stop : function(){
21395         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21396             this.anim.stop();
21397         }
21398     },
21399
21400     /**
21401      * Displays this proxy
21402      */
21403     show : function(){
21404         this.el.show();
21405     },
21406
21407     /**
21408      * Force the Layer to sync its shadow and shim positions to the element
21409      */
21410     sync : function(){
21411         this.el.sync();
21412     },
21413
21414     /**
21415      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21416      * invalid drop operation by the item being dragged.
21417      * @param {Array} xy The XY position of the element ([x, y])
21418      * @param {Function} callback The function to call after the repair is complete
21419      * @param {Object} scope The scope in which to execute the callback
21420      */
21421     repair : function(xy, callback, scope){
21422         this.callback = callback;
21423         this.scope = scope;
21424         if(xy && this.animRepair !== false){
21425             this.el.addClass("x-dd-drag-repair");
21426             this.el.hideUnders(true);
21427             this.anim = this.el.shift({
21428                 duration: this.repairDuration || .5,
21429                 easing: 'easeOut',
21430                 xy: xy,
21431                 stopFx: true,
21432                 callback: this.afterRepair,
21433                 scope: this
21434             });
21435         }else{
21436             this.afterRepair();
21437         }
21438     },
21439
21440     // private
21441     afterRepair : function(){
21442         this.hide(true);
21443         if(typeof this.callback == "function"){
21444             this.callback.call(this.scope || this);
21445         }
21446         this.callback = null;
21447         this.scope = null;
21448     }
21449 };/*
21450  * Based on:
21451  * Ext JS Library 1.1.1
21452  * Copyright(c) 2006-2007, Ext JS, LLC.
21453  *
21454  * Originally Released Under LGPL - original licence link has changed is not relivant.
21455  *
21456  * Fork - LGPL
21457  * <script type="text/javascript">
21458  */
21459
21460 /**
21461  * @class Roo.dd.DragSource
21462  * @extends Roo.dd.DDProxy
21463  * A simple class that provides the basic implementation needed to make any element draggable.
21464  * @constructor
21465  * @param {String/HTMLElement/Element} el The container element
21466  * @param {Object} config
21467  */
21468 Roo.dd.DragSource = function(el, config){
21469     this.el = Roo.get(el);
21470     this.dragData = {};
21471     
21472     Roo.apply(this, config);
21473     
21474     if(!this.proxy){
21475         this.proxy = new Roo.dd.StatusProxy();
21476     }
21477
21478     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21479           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21480     
21481     this.dragging = false;
21482 };
21483
21484 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21485     /**
21486      * @cfg {String} dropAllowed
21487      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21488      */
21489     dropAllowed : "x-dd-drop-ok",
21490     /**
21491      * @cfg {String} dropNotAllowed
21492      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21493      */
21494     dropNotAllowed : "x-dd-drop-nodrop",
21495
21496     /**
21497      * Returns the data object associated with this drag source
21498      * @return {Object} data An object containing arbitrary data
21499      */
21500     getDragData : function(e){
21501         return this.dragData;
21502     },
21503
21504     // private
21505     onDragEnter : function(e, id){
21506         var target = Roo.dd.DragDropMgr.getDDById(id);
21507         this.cachedTarget = target;
21508         if(this.beforeDragEnter(target, e, id) !== false){
21509             if(target.isNotifyTarget){
21510                 var status = target.notifyEnter(this, e, this.dragData);
21511                 this.proxy.setStatus(status);
21512             }else{
21513                 this.proxy.setStatus(this.dropAllowed);
21514             }
21515             
21516             if(this.afterDragEnter){
21517                 /**
21518                  * An empty function by default, but provided so that you can perform a custom action
21519                  * when the dragged item enters the drop target by providing an implementation.
21520                  * @param {Roo.dd.DragDrop} target The drop target
21521                  * @param {Event} e The event object
21522                  * @param {String} id The id of the dragged element
21523                  * @method afterDragEnter
21524                  */
21525                 this.afterDragEnter(target, e, id);
21526             }
21527         }
21528     },
21529
21530     /**
21531      * An empty function by default, but provided so that you can perform a custom action
21532      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21533      * @param {Roo.dd.DragDrop} target The drop target
21534      * @param {Event} e The event object
21535      * @param {String} id The id of the dragged element
21536      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21537      */
21538     beforeDragEnter : function(target, e, id){
21539         return true;
21540     },
21541
21542     // private
21543     alignElWithMouse: function() {
21544         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21545         this.proxy.sync();
21546     },
21547
21548     // private
21549     onDragOver : function(e, id){
21550         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21551         if(this.beforeDragOver(target, e, id) !== false){
21552             if(target.isNotifyTarget){
21553                 var status = target.notifyOver(this, e, this.dragData);
21554                 this.proxy.setStatus(status);
21555             }
21556
21557             if(this.afterDragOver){
21558                 /**
21559                  * An empty function by default, but provided so that you can perform a custom action
21560                  * while the dragged item is over the drop target by providing an implementation.
21561                  * @param {Roo.dd.DragDrop} target The drop target
21562                  * @param {Event} e The event object
21563                  * @param {String} id The id of the dragged element
21564                  * @method afterDragOver
21565                  */
21566                 this.afterDragOver(target, e, id);
21567             }
21568         }
21569     },
21570
21571     /**
21572      * An empty function by default, but provided so that you can perform a custom action
21573      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21574      * @param {Roo.dd.DragDrop} target The drop target
21575      * @param {Event} e The event object
21576      * @param {String} id The id of the dragged element
21577      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21578      */
21579     beforeDragOver : function(target, e, id){
21580         return true;
21581     },
21582
21583     // private
21584     onDragOut : function(e, id){
21585         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21586         if(this.beforeDragOut(target, e, id) !== false){
21587             if(target.isNotifyTarget){
21588                 target.notifyOut(this, e, this.dragData);
21589             }
21590             this.proxy.reset();
21591             if(this.afterDragOut){
21592                 /**
21593                  * An empty function by default, but provided so that you can perform a custom action
21594                  * after the dragged item is dragged out of the target without dropping.
21595                  * @param {Roo.dd.DragDrop} target The drop target
21596                  * @param {Event} e The event object
21597                  * @param {String} id The id of the dragged element
21598                  * @method afterDragOut
21599                  */
21600                 this.afterDragOut(target, e, id);
21601             }
21602         }
21603         this.cachedTarget = null;
21604     },
21605
21606     /**
21607      * An empty function by default, but provided so that you can perform a custom action before the dragged
21608      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21609      * @param {Roo.dd.DragDrop} target The drop target
21610      * @param {Event} e The event object
21611      * @param {String} id The id of the dragged element
21612      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21613      */
21614     beforeDragOut : function(target, e, id){
21615         return true;
21616     },
21617     
21618     // private
21619     onDragDrop : function(e, id){
21620         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21621         if(this.beforeDragDrop(target, e, id) !== false){
21622             if(target.isNotifyTarget){
21623                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21624                     this.onValidDrop(target, e, id);
21625                 }else{
21626                     this.onInvalidDrop(target, e, id);
21627                 }
21628             }else{
21629                 this.onValidDrop(target, e, id);
21630             }
21631             
21632             if(this.afterDragDrop){
21633                 /**
21634                  * An empty function by default, but provided so that you can perform a custom action
21635                  * after a valid drag drop has occurred by providing an implementation.
21636                  * @param {Roo.dd.DragDrop} target The drop target
21637                  * @param {Event} e The event object
21638                  * @param {String} id The id of the dropped element
21639                  * @method afterDragDrop
21640                  */
21641                 this.afterDragDrop(target, e, id);
21642             }
21643         }
21644         delete this.cachedTarget;
21645     },
21646
21647     /**
21648      * An empty function by default, but provided so that you can perform a custom action before the dragged
21649      * item is dropped onto the target and optionally cancel the onDragDrop.
21650      * @param {Roo.dd.DragDrop} target The drop target
21651      * @param {Event} e The event object
21652      * @param {String} id The id of the dragged element
21653      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21654      */
21655     beforeDragDrop : function(target, e, id){
21656         return true;
21657     },
21658
21659     // private
21660     onValidDrop : function(target, e, id){
21661         this.hideProxy();
21662         if(this.afterValidDrop){
21663             /**
21664              * An empty function by default, but provided so that you can perform a custom action
21665              * after a valid drop has occurred by providing an implementation.
21666              * @param {Object} target The target DD 
21667              * @param {Event} e The event object
21668              * @param {String} id The id of the dropped element
21669              * @method afterInvalidDrop
21670              */
21671             this.afterValidDrop(target, e, id);
21672         }
21673     },
21674
21675     // private
21676     getRepairXY : function(e, data){
21677         return this.el.getXY();  
21678     },
21679
21680     // private
21681     onInvalidDrop : function(target, e, id){
21682         this.beforeInvalidDrop(target, e, id);
21683         if(this.cachedTarget){
21684             if(this.cachedTarget.isNotifyTarget){
21685                 this.cachedTarget.notifyOut(this, e, this.dragData);
21686             }
21687             this.cacheTarget = null;
21688         }
21689         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21690
21691         if(this.afterInvalidDrop){
21692             /**
21693              * An empty function by default, but provided so that you can perform a custom action
21694              * after an invalid drop has occurred by providing an implementation.
21695              * @param {Event} e The event object
21696              * @param {String} id The id of the dropped element
21697              * @method afterInvalidDrop
21698              */
21699             this.afterInvalidDrop(e, id);
21700         }
21701     },
21702
21703     // private
21704     afterRepair : function(){
21705         if(Roo.enableFx){
21706             this.el.highlight(this.hlColor || "c3daf9");
21707         }
21708         this.dragging = false;
21709     },
21710
21711     /**
21712      * An empty function by default, but provided so that you can perform a custom action after an invalid
21713      * drop has occurred.
21714      * @param {Roo.dd.DragDrop} target The drop target
21715      * @param {Event} e The event object
21716      * @param {String} id The id of the dragged element
21717      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21718      */
21719     beforeInvalidDrop : function(target, e, id){
21720         return true;
21721     },
21722
21723     // private
21724     handleMouseDown : function(e){
21725         if(this.dragging) {
21726             return;
21727         }
21728         var data = this.getDragData(e);
21729         if(data && this.onBeforeDrag(data, e) !== false){
21730             this.dragData = data;
21731             this.proxy.stop();
21732             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21733         } 
21734     },
21735
21736     /**
21737      * An empty function by default, but provided so that you can perform a custom action before the initial
21738      * drag event begins and optionally cancel it.
21739      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21740      * @param {Event} e The event object
21741      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21742      */
21743     onBeforeDrag : function(data, e){
21744         return true;
21745     },
21746
21747     /**
21748      * An empty function by default, but provided so that you can perform a custom action once the initial
21749      * drag event has begun.  The drag cannot be canceled from this function.
21750      * @param {Number} x The x position of the click on the dragged object
21751      * @param {Number} y The y position of the click on the dragged object
21752      */
21753     onStartDrag : Roo.emptyFn,
21754
21755     // private - YUI override
21756     startDrag : function(x, y){
21757         this.proxy.reset();
21758         this.dragging = true;
21759         this.proxy.update("");
21760         this.onInitDrag(x, y);
21761         this.proxy.show();
21762     },
21763
21764     // private
21765     onInitDrag : function(x, y){
21766         var clone = this.el.dom.cloneNode(true);
21767         clone.id = Roo.id(); // prevent duplicate ids
21768         this.proxy.update(clone);
21769         this.onStartDrag(x, y);
21770         return true;
21771     },
21772
21773     /**
21774      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21775      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21776      */
21777     getProxy : function(){
21778         return this.proxy;  
21779     },
21780
21781     /**
21782      * Hides the drag source's {@link Roo.dd.StatusProxy}
21783      */
21784     hideProxy : function(){
21785         this.proxy.hide();  
21786         this.proxy.reset(true);
21787         this.dragging = false;
21788     },
21789
21790     // private
21791     triggerCacheRefresh : function(){
21792         Roo.dd.DDM.refreshCache(this.groups);
21793     },
21794
21795     // private - override to prevent hiding
21796     b4EndDrag: function(e) {
21797     },
21798
21799     // private - override to prevent moving
21800     endDrag : function(e){
21801         this.onEndDrag(this.dragData, e);
21802     },
21803
21804     // private
21805     onEndDrag : function(data, e){
21806     },
21807     
21808     // private - pin to cursor
21809     autoOffset : function(x, y) {
21810         this.setDelta(-12, -20);
21811     }    
21812 });/*
21813  * Based on:
21814  * Ext JS Library 1.1.1
21815  * Copyright(c) 2006-2007, Ext JS, LLC.
21816  *
21817  * Originally Released Under LGPL - original licence link has changed is not relivant.
21818  *
21819  * Fork - LGPL
21820  * <script type="text/javascript">
21821  */
21822
21823
21824 /**
21825  * @class Roo.dd.DropTarget
21826  * @extends Roo.dd.DDTarget
21827  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21828  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21829  * @constructor
21830  * @param {String/HTMLElement/Element} el The container element
21831  * @param {Object} config
21832  */
21833 Roo.dd.DropTarget = function(el, config){
21834     this.el = Roo.get(el);
21835     
21836     var listeners = false; ;
21837     if (config && config.listeners) {
21838         listeners= config.listeners;
21839         delete config.listeners;
21840     }
21841     Roo.apply(this, config);
21842     
21843     if(this.containerScroll){
21844         Roo.dd.ScrollManager.register(this.el);
21845     }
21846     this.addEvents( {
21847          /**
21848          * @scope Roo.dd.DropTarget
21849          */
21850          
21851          /**
21852          * @event enter
21853          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21854          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21855          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21856          * 
21857          * IMPORTANT : it should set this.overClass and this.dropAllowed
21858          * 
21859          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21860          * @param {Event} e The event
21861          * @param {Object} data An object containing arbitrary data supplied by the drag source
21862          */
21863         "enter" : true,
21864         
21865          /**
21866          * @event over
21867          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21868          * This method will be called on every mouse movement while the drag source is over the drop target.
21869          * This default implementation simply returns the dropAllowed config value.
21870          * 
21871          * IMPORTANT : it should set this.dropAllowed
21872          * 
21873          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21874          * @param {Event} e The event
21875          * @param {Object} data An object containing arbitrary data supplied by the drag source
21876          
21877          */
21878         "over" : true,
21879         /**
21880          * @event out
21881          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21882          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21883          * overClass (if any) from the drop element.
21884          * 
21885          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21886          * @param {Event} e The event
21887          * @param {Object} data An object containing arbitrary data supplied by the drag source
21888          */
21889          "out" : true,
21890          
21891         /**
21892          * @event drop
21893          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21894          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21895          * implementation that does something to process the drop event and returns true so that the drag source's
21896          * repair action does not run.
21897          * 
21898          * IMPORTANT : it should set this.success
21899          * 
21900          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21901          * @param {Event} e The event
21902          * @param {Object} data An object containing arbitrary data supplied by the drag source
21903         */
21904          "drop" : true
21905     });
21906             
21907      
21908     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21909         this.el.dom, 
21910         this.ddGroup || this.group,
21911         {
21912             isTarget: true,
21913             listeners : listeners || {} 
21914            
21915         
21916         }
21917     );
21918
21919 };
21920
21921 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21922     /**
21923      * @cfg {String} overClass
21924      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21925      */
21926      /**
21927      * @cfg {String} ddGroup
21928      * The drag drop group to handle drop events for
21929      */
21930      
21931     /**
21932      * @cfg {String} dropAllowed
21933      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21934      */
21935     dropAllowed : "x-dd-drop-ok",
21936     /**
21937      * @cfg {String} dropNotAllowed
21938      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21939      */
21940     dropNotAllowed : "x-dd-drop-nodrop",
21941     /**
21942      * @cfg {boolean} success
21943      * set this after drop listener.. 
21944      */
21945     success : false,
21946     /**
21947      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21948      * if the drop point is valid for over/enter..
21949      */
21950     valid : false,
21951     // private
21952     isTarget : true,
21953
21954     // private
21955     isNotifyTarget : true,
21956     
21957     /**
21958      * @hide
21959      */
21960     notifyEnter : function(dd, e, data)
21961     {
21962         this.valid = true;
21963         this.fireEvent('enter', dd, e, data);
21964         if(this.overClass){
21965             this.el.addClass(this.overClass);
21966         }
21967         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21968             this.valid ? this.dropAllowed : this.dropNotAllowed
21969         );
21970     },
21971
21972     /**
21973      * @hide
21974      */
21975     notifyOver : function(dd, e, data)
21976     {
21977         this.valid = true;
21978         this.fireEvent('over', dd, e, data);
21979         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21980             this.valid ? this.dropAllowed : this.dropNotAllowed
21981         );
21982     },
21983
21984     /**
21985      * @hide
21986      */
21987     notifyOut : function(dd, e, data)
21988     {
21989         this.fireEvent('out', dd, e, data);
21990         if(this.overClass){
21991             this.el.removeClass(this.overClass);
21992         }
21993     },
21994
21995     /**
21996      * @hide
21997      */
21998     notifyDrop : function(dd, e, data)
21999     {
22000         this.success = false;
22001         this.fireEvent('drop', dd, e, data);
22002         return this.success;
22003     }
22004 });/*
22005  * Based on:
22006  * Ext JS Library 1.1.1
22007  * Copyright(c) 2006-2007, Ext JS, LLC.
22008  *
22009  * Originally Released Under LGPL - original licence link has changed is not relivant.
22010  *
22011  * Fork - LGPL
22012  * <script type="text/javascript">
22013  */
22014
22015
22016 /**
22017  * @class Roo.dd.DragZone
22018  * @extends Roo.dd.DragSource
22019  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22020  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22021  * @constructor
22022  * @param {String/HTMLElement/Element} el The container element
22023  * @param {Object} config
22024  */
22025 Roo.dd.DragZone = function(el, config){
22026     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22027     if(this.containerScroll){
22028         Roo.dd.ScrollManager.register(this.el);
22029     }
22030 };
22031
22032 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22033     /**
22034      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22035      * for auto scrolling during drag operations.
22036      */
22037     /**
22038      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22039      * method after a failed drop (defaults to "c3daf9" - light blue)
22040      */
22041
22042     /**
22043      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22044      * for a valid target to drag based on the mouse down. Override this method
22045      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22046      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22047      * @param {EventObject} e The mouse down event
22048      * @return {Object} The dragData
22049      */
22050     getDragData : function(e){
22051         return Roo.dd.Registry.getHandleFromEvent(e);
22052     },
22053     
22054     /**
22055      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22056      * this.dragData.ddel
22057      * @param {Number} x The x position of the click on the dragged object
22058      * @param {Number} y The y position of the click on the dragged object
22059      * @return {Boolean} true to continue the drag, false to cancel
22060      */
22061     onInitDrag : function(x, y){
22062         this.proxy.update(this.dragData.ddel.cloneNode(true));
22063         this.onStartDrag(x, y);
22064         return true;
22065     },
22066     
22067     /**
22068      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22069      */
22070     afterRepair : function(){
22071         if(Roo.enableFx){
22072             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22073         }
22074         this.dragging = false;
22075     },
22076
22077     /**
22078      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22079      * the XY of this.dragData.ddel
22080      * @param {EventObject} e The mouse up event
22081      * @return {Array} The xy location (e.g. [100, 200])
22082      */
22083     getRepairXY : function(e){
22084         return Roo.Element.fly(this.dragData.ddel).getXY();  
22085     }
22086 });/*
22087  * Based on:
22088  * Ext JS Library 1.1.1
22089  * Copyright(c) 2006-2007, Ext JS, LLC.
22090  *
22091  * Originally Released Under LGPL - original licence link has changed is not relivant.
22092  *
22093  * Fork - LGPL
22094  * <script type="text/javascript">
22095  */
22096 /**
22097  * @class Roo.dd.DropZone
22098  * @extends Roo.dd.DropTarget
22099  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22100  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22101  * @constructor
22102  * @param {String/HTMLElement/Element} el The container element
22103  * @param {Object} config
22104  */
22105 Roo.dd.DropZone = function(el, config){
22106     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22107 };
22108
22109 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22110     /**
22111      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22112      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22113      * provide your own custom lookup.
22114      * @param {Event} e The event
22115      * @return {Object} data The custom data
22116      */
22117     getTargetFromEvent : function(e){
22118         return Roo.dd.Registry.getTargetFromEvent(e);
22119     },
22120
22121     /**
22122      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22123      * that it has registered.  This method has no default implementation and should be overridden to provide
22124      * node-specific processing if necessary.
22125      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22126      * {@link #getTargetFromEvent} for this node)
22127      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22128      * @param {Event} e The event
22129      * @param {Object} data An object containing arbitrary data supplied by the drag source
22130      */
22131     onNodeEnter : function(n, dd, e, data){
22132         
22133     },
22134
22135     /**
22136      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22137      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22138      * overridden to provide the proper feedback.
22139      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22140      * {@link #getTargetFromEvent} for this node)
22141      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22142      * @param {Event} e The event
22143      * @param {Object} data An object containing arbitrary data supplied by the drag source
22144      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22145      * underlying {@link Roo.dd.StatusProxy} can be updated
22146      */
22147     onNodeOver : function(n, dd, e, data){
22148         return this.dropAllowed;
22149     },
22150
22151     /**
22152      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22153      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22154      * node-specific processing if necessary.
22155      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22156      * {@link #getTargetFromEvent} for this node)
22157      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22158      * @param {Event} e The event
22159      * @param {Object} data An object containing arbitrary data supplied by the drag source
22160      */
22161     onNodeOut : function(n, dd, e, data){
22162         
22163     },
22164
22165     /**
22166      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22167      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22168      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22169      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22170      * {@link #getTargetFromEvent} for this node)
22171      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22172      * @param {Event} e The event
22173      * @param {Object} data An object containing arbitrary data supplied by the drag source
22174      * @return {Boolean} True if the drop was valid, else false
22175      */
22176     onNodeDrop : function(n, dd, e, data){
22177         return false;
22178     },
22179
22180     /**
22181      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22182      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22183      * it should be overridden to provide the proper feedback if necessary.
22184      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22185      * @param {Event} e The event
22186      * @param {Object} data An object containing arbitrary data supplied by the drag source
22187      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22188      * underlying {@link Roo.dd.StatusProxy} can be updated
22189      */
22190     onContainerOver : function(dd, e, data){
22191         return this.dropNotAllowed;
22192     },
22193
22194     /**
22195      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22196      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22197      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22198      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22200      * @param {Event} e The event
22201      * @param {Object} data An object containing arbitrary data supplied by the drag source
22202      * @return {Boolean} True if the drop was valid, else false
22203      */
22204     onContainerDrop : function(dd, e, data){
22205         return false;
22206     },
22207
22208     /**
22209      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22210      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22211      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22212      * you should override this method and provide a custom implementation.
22213      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22214      * @param {Event} e The event
22215      * @param {Object} data An object containing arbitrary data supplied by the drag source
22216      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22217      * underlying {@link Roo.dd.StatusProxy} can be updated
22218      */
22219     notifyEnter : function(dd, e, data){
22220         return this.dropNotAllowed;
22221     },
22222
22223     /**
22224      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22225      * This method will be called on every mouse movement while the drag source is over the drop zone.
22226      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22227      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22228      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22229      * registered node, it will call {@link #onContainerOver}.
22230      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22231      * @param {Event} e The event
22232      * @param {Object} data An object containing arbitrary data supplied by the drag source
22233      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22234      * underlying {@link Roo.dd.StatusProxy} can be updated
22235      */
22236     notifyOver : function(dd, e, data){
22237         var n = this.getTargetFromEvent(e);
22238         if(!n){ // not over valid drop target
22239             if(this.lastOverNode){
22240                 this.onNodeOut(this.lastOverNode, dd, e, data);
22241                 this.lastOverNode = null;
22242             }
22243             return this.onContainerOver(dd, e, data);
22244         }
22245         if(this.lastOverNode != n){
22246             if(this.lastOverNode){
22247                 this.onNodeOut(this.lastOverNode, dd, e, data);
22248             }
22249             this.onNodeEnter(n, dd, e, data);
22250             this.lastOverNode = n;
22251         }
22252         return this.onNodeOver(n, dd, e, data);
22253     },
22254
22255     /**
22256      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22257      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22258      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22259      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22260      * @param {Event} e The event
22261      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22262      */
22263     notifyOut : function(dd, e, data){
22264         if(this.lastOverNode){
22265             this.onNodeOut(this.lastOverNode, dd, e, data);
22266             this.lastOverNode = null;
22267         }
22268     },
22269
22270     /**
22271      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22272      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22273      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22274      * otherwise it will call {@link #onContainerDrop}.
22275      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22276      * @param {Event} e The event
22277      * @param {Object} data An object containing arbitrary data supplied by the drag source
22278      * @return {Boolean} True if the drop was valid, else false
22279      */
22280     notifyDrop : function(dd, e, data){
22281         if(this.lastOverNode){
22282             this.onNodeOut(this.lastOverNode, dd, e, data);
22283             this.lastOverNode = null;
22284         }
22285         var n = this.getTargetFromEvent(e);
22286         return n ?
22287             this.onNodeDrop(n, dd, e, data) :
22288             this.onContainerDrop(dd, e, data);
22289     },
22290
22291     // private
22292     triggerCacheRefresh : function(){
22293         Roo.dd.DDM.refreshCache(this.groups);
22294     }  
22295 });/*
22296  * Based on:
22297  * Ext JS Library 1.1.1
22298  * Copyright(c) 2006-2007, Ext JS, LLC.
22299  *
22300  * Originally Released Under LGPL - original licence link has changed is not relivant.
22301  *
22302  * Fork - LGPL
22303  * <script type="text/javascript">
22304  */
22305
22306
22307 /**
22308  * @class Roo.data.SortTypes
22309  * @singleton
22310  * Defines the default sorting (casting?) comparison functions used when sorting data.
22311  */
22312 Roo.data.SortTypes = {
22313     /**
22314      * Default sort that does nothing
22315      * @param {Mixed} s The value being converted
22316      * @return {Mixed} The comparison value
22317      */
22318     none : function(s){
22319         return s;
22320     },
22321     
22322     /**
22323      * The regular expression used to strip tags
22324      * @type {RegExp}
22325      * @property
22326      */
22327     stripTagsRE : /<\/?[^>]+>/gi,
22328     
22329     /**
22330      * Strips all HTML tags to sort on text only
22331      * @param {Mixed} s The value being converted
22332      * @return {String} The comparison value
22333      */
22334     asText : function(s){
22335         return String(s).replace(this.stripTagsRE, "");
22336     },
22337     
22338     /**
22339      * Strips all HTML tags to sort on text only - Case insensitive
22340      * @param {Mixed} s The value being converted
22341      * @return {String} The comparison value
22342      */
22343     asUCText : function(s){
22344         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22345     },
22346     
22347     /**
22348      * Case insensitive string
22349      * @param {Mixed} s The value being converted
22350      * @return {String} The comparison value
22351      */
22352     asUCString : function(s) {
22353         return String(s).toUpperCase();
22354     },
22355     
22356     /**
22357      * Date sorting
22358      * @param {Mixed} s The value being converted
22359      * @return {Number} The comparison value
22360      */
22361     asDate : function(s) {
22362         if(!s){
22363             return 0;
22364         }
22365         if(s instanceof Date){
22366             return s.getTime();
22367         }
22368         return Date.parse(String(s));
22369     },
22370     
22371     /**
22372      * Float sorting
22373      * @param {Mixed} s The value being converted
22374      * @return {Float} The comparison value
22375      */
22376     asFloat : function(s) {
22377         var val = parseFloat(String(s).replace(/,/g, ""));
22378         if(isNaN(val)) {
22379             val = 0;
22380         }
22381         return val;
22382     },
22383     
22384     /**
22385      * Integer sorting
22386      * @param {Mixed} s The value being converted
22387      * @return {Number} The comparison value
22388      */
22389     asInt : function(s) {
22390         var val = parseInt(String(s).replace(/,/g, ""));
22391         if(isNaN(val)) {
22392             val = 0;
22393         }
22394         return val;
22395     }
22396 };/*
22397  * Based on:
22398  * Ext JS Library 1.1.1
22399  * Copyright(c) 2006-2007, Ext JS, LLC.
22400  *
22401  * Originally Released Under LGPL - original licence link has changed is not relivant.
22402  *
22403  * Fork - LGPL
22404  * <script type="text/javascript">
22405  */
22406
22407 /**
22408 * @class Roo.data.Record
22409  * Instances of this class encapsulate both record <em>definition</em> information, and record
22410  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22411  * to access Records cached in an {@link Roo.data.Store} object.<br>
22412  * <p>
22413  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22414  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22415  * objects.<br>
22416  * <p>
22417  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22418  * @constructor
22419  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22420  * {@link #create}. The parameters are the same.
22421  * @param {Array} data An associative Array of data values keyed by the field name.
22422  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22423  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22424  * not specified an integer id is generated.
22425  */
22426 Roo.data.Record = function(data, id){
22427     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22428     this.data = data;
22429 };
22430
22431 /**
22432  * Generate a constructor for a specific record layout.
22433  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22434  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22435  * Each field definition object may contain the following properties: <ul>
22436  * <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,
22437  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22438  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22439  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22440  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22441  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22442  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22443  * this may be omitted.</p></li>
22444  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22445  * <ul><li>auto (Default, implies no conversion)</li>
22446  * <li>string</li>
22447  * <li>int</li>
22448  * <li>float</li>
22449  * <li>boolean</li>
22450  * <li>date</li></ul></p></li>
22451  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22452  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22453  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22454  * by the Reader into an object that will be stored in the Record. It is passed the
22455  * following parameters:<ul>
22456  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22457  * </ul></p></li>
22458  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22459  * </ul>
22460  * <br>usage:<br><pre><code>
22461 var TopicRecord = Roo.data.Record.create(
22462     {name: 'title', mapping: 'topic_title'},
22463     {name: 'author', mapping: 'username'},
22464     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22465     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22466     {name: 'lastPoster', mapping: 'user2'},
22467     {name: 'excerpt', mapping: 'post_text'}
22468 );
22469
22470 var myNewRecord = new TopicRecord({
22471     title: 'Do my job please',
22472     author: 'noobie',
22473     totalPosts: 1,
22474     lastPost: new Date(),
22475     lastPoster: 'Animal',
22476     excerpt: 'No way dude!'
22477 });
22478 myStore.add(myNewRecord);
22479 </code></pre>
22480  * @method create
22481  * @static
22482  */
22483 Roo.data.Record.create = function(o){
22484     var f = function(){
22485         f.superclass.constructor.apply(this, arguments);
22486     };
22487     Roo.extend(f, Roo.data.Record);
22488     var p = f.prototype;
22489     p.fields = new Roo.util.MixedCollection(false, function(field){
22490         return field.name;
22491     });
22492     for(var i = 0, len = o.length; i < len; i++){
22493         p.fields.add(new Roo.data.Field(o[i]));
22494     }
22495     f.getField = function(name){
22496         return p.fields.get(name);  
22497     };
22498     return f;
22499 };
22500
22501 Roo.data.Record.AUTO_ID = 1000;
22502 Roo.data.Record.EDIT = 'edit';
22503 Roo.data.Record.REJECT = 'reject';
22504 Roo.data.Record.COMMIT = 'commit';
22505
22506 Roo.data.Record.prototype = {
22507     /**
22508      * Readonly flag - true if this record has been modified.
22509      * @type Boolean
22510      */
22511     dirty : false,
22512     editing : false,
22513     error: null,
22514     modified: null,
22515
22516     // private
22517     join : function(store){
22518         this.store = store;
22519     },
22520
22521     /**
22522      * Set the named field to the specified value.
22523      * @param {String} name The name of the field to set.
22524      * @param {Object} value The value to set the field to.
22525      */
22526     set : function(name, value){
22527         if(this.data[name] == value){
22528             return;
22529         }
22530         this.dirty = true;
22531         if(!this.modified){
22532             this.modified = {};
22533         }
22534         if(typeof this.modified[name] == 'undefined'){
22535             this.modified[name] = this.data[name];
22536         }
22537         this.data[name] = value;
22538         if(!this.editing && this.store){
22539             this.store.afterEdit(this);
22540         }       
22541     },
22542
22543     /**
22544      * Get the value of the named field.
22545      * @param {String} name The name of the field to get the value of.
22546      * @return {Object} The value of the field.
22547      */
22548     get : function(name){
22549         return this.data[name]; 
22550     },
22551
22552     // private
22553     beginEdit : function(){
22554         this.editing = true;
22555         this.modified = {}; 
22556     },
22557
22558     // private
22559     cancelEdit : function(){
22560         this.editing = false;
22561         delete this.modified;
22562     },
22563
22564     // private
22565     endEdit : function(){
22566         this.editing = false;
22567         if(this.dirty && this.store){
22568             this.store.afterEdit(this);
22569         }
22570     },
22571
22572     /**
22573      * Usually called by the {@link Roo.data.Store} which owns the Record.
22574      * Rejects all changes made to the Record since either creation, or the last commit operation.
22575      * Modified fields are reverted to their original values.
22576      * <p>
22577      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22578      * of reject operations.
22579      */
22580     reject : function(){
22581         var m = this.modified;
22582         for(var n in m){
22583             if(typeof m[n] != "function"){
22584                 this.data[n] = m[n];
22585             }
22586         }
22587         this.dirty = false;
22588         delete this.modified;
22589         this.editing = false;
22590         if(this.store){
22591             this.store.afterReject(this);
22592         }
22593     },
22594
22595     /**
22596      * Usually called by the {@link Roo.data.Store} which owns the Record.
22597      * Commits all changes made to the Record since either creation, or the last commit operation.
22598      * <p>
22599      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22600      * of commit operations.
22601      */
22602     commit : function(){
22603         this.dirty = false;
22604         delete this.modified;
22605         this.editing = false;
22606         if(this.store){
22607             this.store.afterCommit(this);
22608         }
22609     },
22610
22611     // private
22612     hasError : function(){
22613         return this.error != null;
22614     },
22615
22616     // private
22617     clearError : function(){
22618         this.error = null;
22619     },
22620
22621     /**
22622      * Creates a copy of this record.
22623      * @param {String} id (optional) A new record id if you don't want to use this record's id
22624      * @return {Record}
22625      */
22626     copy : function(newId) {
22627         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22628     }
22629 };/*
22630  * Based on:
22631  * Ext JS Library 1.1.1
22632  * Copyright(c) 2006-2007, Ext JS, LLC.
22633  *
22634  * Originally Released Under LGPL - original licence link has changed is not relivant.
22635  *
22636  * Fork - LGPL
22637  * <script type="text/javascript">
22638  */
22639
22640
22641
22642 /**
22643  * @class Roo.data.Store
22644  * @extends Roo.util.Observable
22645  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22646  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22647  * <p>
22648  * 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
22649  * has no knowledge of the format of the data returned by the Proxy.<br>
22650  * <p>
22651  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22652  * instances from the data object. These records are cached and made available through accessor functions.
22653  * @constructor
22654  * Creates a new Store.
22655  * @param {Object} config A config object containing the objects needed for the Store to access data,
22656  * and read the data into Records.
22657  */
22658 Roo.data.Store = function(config){
22659     this.data = new Roo.util.MixedCollection(false);
22660     this.data.getKey = function(o){
22661         return o.id;
22662     };
22663     this.baseParams = {};
22664     // private
22665     this.paramNames = {
22666         "start" : "start",
22667         "limit" : "limit",
22668         "sort" : "sort",
22669         "dir" : "dir",
22670         "multisort" : "_multisort"
22671     };
22672
22673     if(config && config.data){
22674         this.inlineData = config.data;
22675         delete config.data;
22676     }
22677
22678     Roo.apply(this, config);
22679     
22680     if(this.reader){ // reader passed
22681         this.reader = Roo.factory(this.reader, Roo.data);
22682         this.reader.xmodule = this.xmodule || false;
22683         if(!this.recordType){
22684             this.recordType = this.reader.recordType;
22685         }
22686         if(this.reader.onMetaChange){
22687             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22688         }
22689     }
22690
22691     if(this.recordType){
22692         this.fields = this.recordType.prototype.fields;
22693     }
22694     this.modified = [];
22695
22696     this.addEvents({
22697         /**
22698          * @event datachanged
22699          * Fires when the data cache has changed, and a widget which is using this Store
22700          * as a Record cache should refresh its view.
22701          * @param {Store} this
22702          */
22703         datachanged : true,
22704         /**
22705          * @event metachange
22706          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22707          * @param {Store} this
22708          * @param {Object} meta The JSON metadata
22709          */
22710         metachange : true,
22711         /**
22712          * @event add
22713          * Fires when Records have been added to the Store
22714          * @param {Store} this
22715          * @param {Roo.data.Record[]} records The array of Records added
22716          * @param {Number} index The index at which the record(s) were added
22717          */
22718         add : true,
22719         /**
22720          * @event remove
22721          * Fires when a Record has been removed from the Store
22722          * @param {Store} this
22723          * @param {Roo.data.Record} record The Record that was removed
22724          * @param {Number} index The index at which the record was removed
22725          */
22726         remove : true,
22727         /**
22728          * @event update
22729          * Fires when a Record has been updated
22730          * @param {Store} this
22731          * @param {Roo.data.Record} record The Record that was updated
22732          * @param {String} operation The update operation being performed.  Value may be one of:
22733          * <pre><code>
22734  Roo.data.Record.EDIT
22735  Roo.data.Record.REJECT
22736  Roo.data.Record.COMMIT
22737          * </code></pre>
22738          */
22739         update : true,
22740         /**
22741          * @event clear
22742          * Fires when the data cache has been cleared.
22743          * @param {Store} this
22744          */
22745         clear : true,
22746         /**
22747          * @event beforeload
22748          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22749          * the load action will be canceled.
22750          * @param {Store} this
22751          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22752          */
22753         beforeload : true,
22754         /**
22755          * @event beforeloadadd
22756          * Fires after a new set of Records has been loaded.
22757          * @param {Store} this
22758          * @param {Roo.data.Record[]} records The Records that were loaded
22759          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22760          */
22761         beforeloadadd : true,
22762         /**
22763          * @event load
22764          * Fires after a new set of Records has been loaded, before they are added to the store.
22765          * @param {Store} this
22766          * @param {Roo.data.Record[]} records The Records that were loaded
22767          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22768          * @params {Object} return from reader
22769          */
22770         load : true,
22771         /**
22772          * @event loadexception
22773          * Fires if an exception occurs in the Proxy during loading.
22774          * Called with the signature of the Proxy's "loadexception" event.
22775          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22776          * 
22777          * @param {Proxy} 
22778          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22779          * @param {Object} load options 
22780          * @param {Object} jsonData from your request (normally this contains the Exception)
22781          */
22782         loadexception : true
22783     });
22784     
22785     if(this.proxy){
22786         this.proxy = Roo.factory(this.proxy, Roo.data);
22787         this.proxy.xmodule = this.xmodule || false;
22788         this.relayEvents(this.proxy,  ["loadexception"]);
22789     }
22790     this.sortToggle = {};
22791     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22792
22793     Roo.data.Store.superclass.constructor.call(this);
22794
22795     if(this.inlineData){
22796         this.loadData(this.inlineData);
22797         delete this.inlineData;
22798     }
22799 };
22800
22801 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22802      /**
22803     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22804     * without a remote query - used by combo/forms at present.
22805     */
22806     
22807     /**
22808     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22809     */
22810     /**
22811     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22812     */
22813     /**
22814     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22815     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22816     */
22817     /**
22818     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22819     * on any HTTP request
22820     */
22821     /**
22822     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22823     */
22824     /**
22825     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22826     */
22827     multiSort: false,
22828     /**
22829     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22830     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22831     */
22832     remoteSort : false,
22833
22834     /**
22835     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22836      * loaded or when a record is removed. (defaults to false).
22837     */
22838     pruneModifiedRecords : false,
22839
22840     // private
22841     lastOptions : null,
22842
22843     /**
22844      * Add Records to the Store and fires the add event.
22845      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22846      */
22847     add : function(records){
22848         records = [].concat(records);
22849         for(var i = 0, len = records.length; i < len; i++){
22850             records[i].join(this);
22851         }
22852         var index = this.data.length;
22853         this.data.addAll(records);
22854         this.fireEvent("add", this, records, index);
22855     },
22856
22857     /**
22858      * Remove a Record from the Store and fires the remove event.
22859      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22860      */
22861     remove : function(record){
22862         var index = this.data.indexOf(record);
22863         this.data.removeAt(index);
22864         if(this.pruneModifiedRecords){
22865             this.modified.remove(record);
22866         }
22867         this.fireEvent("remove", this, record, index);
22868     },
22869
22870     /**
22871      * Remove all Records from the Store and fires the clear event.
22872      */
22873     removeAll : function(){
22874         this.data.clear();
22875         if(this.pruneModifiedRecords){
22876             this.modified = [];
22877         }
22878         this.fireEvent("clear", this);
22879     },
22880
22881     /**
22882      * Inserts Records to the Store at the given index and fires the add event.
22883      * @param {Number} index The start index at which to insert the passed Records.
22884      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22885      */
22886     insert : function(index, records){
22887         records = [].concat(records);
22888         for(var i = 0, len = records.length; i < len; i++){
22889             this.data.insert(index, records[i]);
22890             records[i].join(this);
22891         }
22892         this.fireEvent("add", this, records, index);
22893     },
22894
22895     /**
22896      * Get the index within the cache of the passed Record.
22897      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22898      * @return {Number} The index of the passed Record. Returns -1 if not found.
22899      */
22900     indexOf : function(record){
22901         return this.data.indexOf(record);
22902     },
22903
22904     /**
22905      * Get the index within the cache of the Record with the passed id.
22906      * @param {String} id The id of the Record to find.
22907      * @return {Number} The index of the Record. Returns -1 if not found.
22908      */
22909     indexOfId : function(id){
22910         return this.data.indexOfKey(id);
22911     },
22912
22913     /**
22914      * Get the Record with the specified id.
22915      * @param {String} id The id of the Record to find.
22916      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22917      */
22918     getById : function(id){
22919         return this.data.key(id);
22920     },
22921
22922     /**
22923      * Get the Record at the specified index.
22924      * @param {Number} index The index of the Record to find.
22925      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22926      */
22927     getAt : function(index){
22928         return this.data.itemAt(index);
22929     },
22930
22931     /**
22932      * Returns a range of Records between specified indices.
22933      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22934      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22935      * @return {Roo.data.Record[]} An array of Records
22936      */
22937     getRange : function(start, end){
22938         return this.data.getRange(start, end);
22939     },
22940
22941     // private
22942     storeOptions : function(o){
22943         o = Roo.apply({}, o);
22944         delete o.callback;
22945         delete o.scope;
22946         this.lastOptions = o;
22947     },
22948
22949     /**
22950      * Loads the Record cache from the configured Proxy using the configured Reader.
22951      * <p>
22952      * If using remote paging, then the first load call must specify the <em>start</em>
22953      * and <em>limit</em> properties in the options.params property to establish the initial
22954      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22955      * <p>
22956      * <strong>It is important to note that for remote data sources, loading is asynchronous,
22957      * and this call will return before the new data has been loaded. Perform any post-processing
22958      * in a callback function, or in a "load" event handler.</strong>
22959      * <p>
22960      * @param {Object} options An object containing properties which control loading options:<ul>
22961      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
22962      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
22963      * passed the following arguments:<ul>
22964      * <li>r : Roo.data.Record[]</li>
22965      * <li>options: Options object from the load call</li>
22966      * <li>success: Boolean success indicator</li></ul></li>
22967      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
22968      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
22969      * </ul>
22970      */
22971     load : function(options){
22972         options = options || {};
22973         if(this.fireEvent("beforeload", this, options) !== false){
22974             this.storeOptions(options);
22975             var p = Roo.apply(options.params || {}, this.baseParams);
22976             // if meta was not loaded from remote source.. try requesting it.
22977             if (!this.reader.metaFromRemote) {
22978                 p._requestMeta = 1;
22979             }
22980             if(this.sortInfo && this.remoteSort){
22981                 var pn = this.paramNames;
22982                 p[pn["sort"]] = this.sortInfo.field;
22983                 p[pn["dir"]] = this.sortInfo.direction;
22984             }
22985             if (this.multiSort) {
22986                 var pn = this.paramNames;
22987                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
22988             }
22989             
22990             this.proxy.load(p, this.reader, this.loadRecords, this, options);
22991         }
22992     },
22993
22994     /**
22995      * Reloads the Record cache from the configured Proxy using the configured Reader and
22996      * the options from the last load operation performed.
22997      * @param {Object} options (optional) An object containing properties which may override the options
22998      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
22999      * the most recently used options are reused).
23000      */
23001     reload : function(options){
23002         this.load(Roo.applyIf(options||{}, this.lastOptions));
23003     },
23004
23005     // private
23006     // Called as a callback by the Reader during a load operation.
23007     loadRecords : function(o, options, success){
23008         if(!o || success === false){
23009             if(success !== false){
23010                 this.fireEvent("load", this, [], options, o);
23011             }
23012             if(options.callback){
23013                 options.callback.call(options.scope || this, [], options, false);
23014             }
23015             return;
23016         }
23017         // if data returned failure - throw an exception.
23018         if (o.success === false) {
23019             // show a message if no listener is registered.
23020             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23021                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23022             }
23023             // loadmask wil be hooked into this..
23024             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23025             return;
23026         }
23027         var r = o.records, t = o.totalRecords || r.length;
23028         
23029         this.fireEvent("beforeloadadd", this, r, options, o);
23030         
23031         if(!options || options.add !== true){
23032             if(this.pruneModifiedRecords){
23033                 this.modified = [];
23034             }
23035             for(var i = 0, len = r.length; i < len; i++){
23036                 r[i].join(this);
23037             }
23038             if(this.snapshot){
23039                 this.data = this.snapshot;
23040                 delete this.snapshot;
23041             }
23042             this.data.clear();
23043             this.data.addAll(r);
23044             this.totalLength = t;
23045             this.applySort();
23046             this.fireEvent("datachanged", this);
23047         }else{
23048             this.totalLength = Math.max(t, this.data.length+r.length);
23049             this.add(r);
23050         }
23051         this.fireEvent("load", this, r, options, o);
23052         if(options.callback){
23053             options.callback.call(options.scope || this, r, options, true);
23054         }
23055     },
23056
23057
23058     /**
23059      * Loads data from a passed data block. A Reader which understands the format of the data
23060      * must have been configured in the constructor.
23061      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23062      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23063      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23064      */
23065     loadData : function(o, append){
23066         var r = this.reader.readRecords(o);
23067         this.loadRecords(r, {add: append}, true);
23068     },
23069
23070     /**
23071      * Gets the number of cached records.
23072      * <p>
23073      * <em>If using paging, this may not be the total size of the dataset. If the data object
23074      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23075      * the data set size</em>
23076      */
23077     getCount : function(){
23078         return this.data.length || 0;
23079     },
23080
23081     /**
23082      * Gets the total number of records in the dataset as returned by the server.
23083      * <p>
23084      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23085      * the dataset size</em>
23086      */
23087     getTotalCount : function(){
23088         return this.totalLength || 0;
23089     },
23090
23091     /**
23092      * Returns the sort state of the Store as an object with two properties:
23093      * <pre><code>
23094  field {String} The name of the field by which the Records are sorted
23095  direction {String} The sort order, "ASC" or "DESC"
23096      * </code></pre>
23097      */
23098     getSortState : function(){
23099         return this.sortInfo;
23100     },
23101
23102     // private
23103     applySort : function(){
23104         if(this.sortInfo && !this.remoteSort){
23105             var s = this.sortInfo, f = s.field;
23106             var st = this.fields.get(f).sortType;
23107             var fn = function(r1, r2){
23108                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23109                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23110             };
23111             this.data.sort(s.direction, fn);
23112             if(this.snapshot && this.snapshot != this.data){
23113                 this.snapshot.sort(s.direction, fn);
23114             }
23115         }
23116     },
23117
23118     /**
23119      * Sets the default sort column and order to be used by the next load operation.
23120      * @param {String} fieldName The name of the field to sort by.
23121      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23122      */
23123     setDefaultSort : function(field, dir){
23124         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23125     },
23126
23127     /**
23128      * Sort the Records.
23129      * If remote sorting is used, the sort is performed on the server, and the cache is
23130      * reloaded. If local sorting is used, the cache is sorted internally.
23131      * @param {String} fieldName The name of the field to sort by.
23132      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23133      */
23134     sort : function(fieldName, dir){
23135         var f = this.fields.get(fieldName);
23136         if(!dir){
23137             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23138             
23139             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23140                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23141             }else{
23142                 dir = f.sortDir;
23143             }
23144         }
23145         this.sortToggle[f.name] = dir;
23146         this.sortInfo = {field: f.name, direction: dir};
23147         if(!this.remoteSort){
23148             this.applySort();
23149             this.fireEvent("datachanged", this);
23150         }else{
23151             this.load(this.lastOptions);
23152         }
23153     },
23154
23155     /**
23156      * Calls the specified function for each of the Records in the cache.
23157      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23158      * Returning <em>false</em> aborts and exits the iteration.
23159      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23160      */
23161     each : function(fn, scope){
23162         this.data.each(fn, scope);
23163     },
23164
23165     /**
23166      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23167      * (e.g., during paging).
23168      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23169      */
23170     getModifiedRecords : function(){
23171         return this.modified;
23172     },
23173
23174     // private
23175     createFilterFn : function(property, value, anyMatch){
23176         if(!value.exec){ // not a regex
23177             value = String(value);
23178             if(value.length == 0){
23179                 return false;
23180             }
23181             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23182         }
23183         return function(r){
23184             return value.test(r.data[property]);
23185         };
23186     },
23187
23188     /**
23189      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23190      * @param {String} property A field on your records
23191      * @param {Number} start The record index to start at (defaults to 0)
23192      * @param {Number} end The last record index to include (defaults to length - 1)
23193      * @return {Number} The sum
23194      */
23195     sum : function(property, start, end){
23196         var rs = this.data.items, v = 0;
23197         start = start || 0;
23198         end = (end || end === 0) ? end : rs.length-1;
23199
23200         for(var i = start; i <= end; i++){
23201             v += (rs[i].data[property] || 0);
23202         }
23203         return v;
23204     },
23205
23206     /**
23207      * Filter the records by a specified property.
23208      * @param {String} field A field on your records
23209      * @param {String/RegExp} value Either a string that the field
23210      * should start with or a RegExp to test against the field
23211      * @param {Boolean} anyMatch True to match any part not just the beginning
23212      */
23213     filter : function(property, value, anyMatch){
23214         var fn = this.createFilterFn(property, value, anyMatch);
23215         return fn ? this.filterBy(fn) : this.clearFilter();
23216     },
23217
23218     /**
23219      * Filter by a function. The specified function will be called with each
23220      * record in this data source. If the function returns true the record is included,
23221      * otherwise it is filtered.
23222      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23223      * @param {Object} scope (optional) The scope of the function (defaults to this)
23224      */
23225     filterBy : function(fn, scope){
23226         this.snapshot = this.snapshot || this.data;
23227         this.data = this.queryBy(fn, scope||this);
23228         this.fireEvent("datachanged", this);
23229     },
23230
23231     /**
23232      * Query the records by a specified property.
23233      * @param {String} field A field on your records
23234      * @param {String/RegExp} value Either a string that the field
23235      * should start with or a RegExp to test against the field
23236      * @param {Boolean} anyMatch True to match any part not just the beginning
23237      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23238      */
23239     query : function(property, value, anyMatch){
23240         var fn = this.createFilterFn(property, value, anyMatch);
23241         return fn ? this.queryBy(fn) : this.data.clone();
23242     },
23243
23244     /**
23245      * Query by a function. The specified function will be called with each
23246      * record in this data source. If the function returns true the record is included
23247      * in the results.
23248      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23249      * @param {Object} scope (optional) The scope of the function (defaults to this)
23250       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23251      **/
23252     queryBy : function(fn, scope){
23253         var data = this.snapshot || this.data;
23254         return data.filterBy(fn, scope||this);
23255     },
23256
23257     /**
23258      * Collects unique values for a particular dataIndex from this store.
23259      * @param {String} dataIndex The property to collect
23260      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23261      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23262      * @return {Array} An array of the unique values
23263      **/
23264     collect : function(dataIndex, allowNull, bypassFilter){
23265         var d = (bypassFilter === true && this.snapshot) ?
23266                 this.snapshot.items : this.data.items;
23267         var v, sv, r = [], l = {};
23268         for(var i = 0, len = d.length; i < len; i++){
23269             v = d[i].data[dataIndex];
23270             sv = String(v);
23271             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23272                 l[sv] = true;
23273                 r[r.length] = v;
23274             }
23275         }
23276         return r;
23277     },
23278
23279     /**
23280      * Revert to a view of the Record cache with no filtering applied.
23281      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23282      */
23283     clearFilter : function(suppressEvent){
23284         if(this.snapshot && this.snapshot != this.data){
23285             this.data = this.snapshot;
23286             delete this.snapshot;
23287             if(suppressEvent !== true){
23288                 this.fireEvent("datachanged", this);
23289             }
23290         }
23291     },
23292
23293     // private
23294     afterEdit : function(record){
23295         if(this.modified.indexOf(record) == -1){
23296             this.modified.push(record);
23297         }
23298         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23299     },
23300     
23301     // private
23302     afterReject : function(record){
23303         this.modified.remove(record);
23304         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23305     },
23306
23307     // private
23308     afterCommit : function(record){
23309         this.modified.remove(record);
23310         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23311     },
23312
23313     /**
23314      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23315      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23316      */
23317     commitChanges : function(){
23318         var m = this.modified.slice(0);
23319         this.modified = [];
23320         for(var i = 0, len = m.length; i < len; i++){
23321             m[i].commit();
23322         }
23323     },
23324
23325     /**
23326      * Cancel outstanding changes on all changed records.
23327      */
23328     rejectChanges : function(){
23329         var m = this.modified.slice(0);
23330         this.modified = [];
23331         for(var i = 0, len = m.length; i < len; i++){
23332             m[i].reject();
23333         }
23334     },
23335
23336     onMetaChange : function(meta, rtype, o){
23337         this.recordType = rtype;
23338         this.fields = rtype.prototype.fields;
23339         delete this.snapshot;
23340         this.sortInfo = meta.sortInfo || this.sortInfo;
23341         this.modified = [];
23342         this.fireEvent('metachange', this, this.reader.meta);
23343     },
23344     
23345     moveIndex : function(data, type)
23346     {
23347         var index = this.indexOf(data);
23348         
23349         var newIndex = index + type;
23350         
23351         this.remove(data);
23352         
23353         this.insert(newIndex, data);
23354         
23355     }
23356 });/*
23357  * Based on:
23358  * Ext JS Library 1.1.1
23359  * Copyright(c) 2006-2007, Ext JS, LLC.
23360  *
23361  * Originally Released Under LGPL - original licence link has changed is not relivant.
23362  *
23363  * Fork - LGPL
23364  * <script type="text/javascript">
23365  */
23366
23367 /**
23368  * @class Roo.data.SimpleStore
23369  * @extends Roo.data.Store
23370  * Small helper class to make creating Stores from Array data easier.
23371  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23372  * @cfg {Array} fields An array of field definition objects, or field name strings.
23373  * @cfg {Array} data The multi-dimensional array of data
23374  * @constructor
23375  * @param {Object} config
23376  */
23377 Roo.data.SimpleStore = function(config){
23378     Roo.data.SimpleStore.superclass.constructor.call(this, {
23379         isLocal : true,
23380         reader: new Roo.data.ArrayReader({
23381                 id: config.id
23382             },
23383             Roo.data.Record.create(config.fields)
23384         ),
23385         proxy : new Roo.data.MemoryProxy(config.data)
23386     });
23387     this.load();
23388 };
23389 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23390  * Based on:
23391  * Ext JS Library 1.1.1
23392  * Copyright(c) 2006-2007, Ext JS, LLC.
23393  *
23394  * Originally Released Under LGPL - original licence link has changed is not relivant.
23395  *
23396  * Fork - LGPL
23397  * <script type="text/javascript">
23398  */
23399
23400 /**
23401 /**
23402  * @extends Roo.data.Store
23403  * @class Roo.data.JsonStore
23404  * Small helper class to make creating Stores for JSON data easier. <br/>
23405 <pre><code>
23406 var store = new Roo.data.JsonStore({
23407     url: 'get-images.php',
23408     root: 'images',
23409     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23410 });
23411 </code></pre>
23412  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23413  * JsonReader and HttpProxy (unless inline data is provided).</b>
23414  * @cfg {Array} fields An array of field definition objects, or field name strings.
23415  * @constructor
23416  * @param {Object} config
23417  */
23418 Roo.data.JsonStore = function(c){
23419     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23420         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23421         reader: new Roo.data.JsonReader(c, c.fields)
23422     }));
23423 };
23424 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23425  * Based on:
23426  * Ext JS Library 1.1.1
23427  * Copyright(c) 2006-2007, Ext JS, LLC.
23428  *
23429  * Originally Released Under LGPL - original licence link has changed is not relivant.
23430  *
23431  * Fork - LGPL
23432  * <script type="text/javascript">
23433  */
23434
23435  
23436 Roo.data.Field = function(config){
23437     if(typeof config == "string"){
23438         config = {name: config};
23439     }
23440     Roo.apply(this, config);
23441     
23442     if(!this.type){
23443         this.type = "auto";
23444     }
23445     
23446     var st = Roo.data.SortTypes;
23447     // named sortTypes are supported, here we look them up
23448     if(typeof this.sortType == "string"){
23449         this.sortType = st[this.sortType];
23450     }
23451     
23452     // set default sortType for strings and dates
23453     if(!this.sortType){
23454         switch(this.type){
23455             case "string":
23456                 this.sortType = st.asUCString;
23457                 break;
23458             case "date":
23459                 this.sortType = st.asDate;
23460                 break;
23461             default:
23462                 this.sortType = st.none;
23463         }
23464     }
23465
23466     // define once
23467     var stripRe = /[\$,%]/g;
23468
23469     // prebuilt conversion function for this field, instead of
23470     // switching every time we're reading a value
23471     if(!this.convert){
23472         var cv, dateFormat = this.dateFormat;
23473         switch(this.type){
23474             case "":
23475             case "auto":
23476             case undefined:
23477                 cv = function(v){ return v; };
23478                 break;
23479             case "string":
23480                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23481                 break;
23482             case "int":
23483                 cv = function(v){
23484                     return v !== undefined && v !== null && v !== '' ?
23485                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23486                     };
23487                 break;
23488             case "float":
23489                 cv = function(v){
23490                     return v !== undefined && v !== null && v !== '' ?
23491                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23492                     };
23493                 break;
23494             case "bool":
23495             case "boolean":
23496                 cv = function(v){ return v === true || v === "true" || v == 1; };
23497                 break;
23498             case "date":
23499                 cv = function(v){
23500                     if(!v){
23501                         return '';
23502                     }
23503                     if(v instanceof Date){
23504                         return v;
23505                     }
23506                     if(dateFormat){
23507                         if(dateFormat == "timestamp"){
23508                             return new Date(v*1000);
23509                         }
23510                         return Date.parseDate(v, dateFormat);
23511                     }
23512                     var parsed = Date.parse(v);
23513                     return parsed ? new Date(parsed) : null;
23514                 };
23515              break;
23516             
23517         }
23518         this.convert = cv;
23519     }
23520 };
23521
23522 Roo.data.Field.prototype = {
23523     dateFormat: null,
23524     defaultValue: "",
23525     mapping: null,
23526     sortType : null,
23527     sortDir : "ASC"
23528 };/*
23529  * Based on:
23530  * Ext JS Library 1.1.1
23531  * Copyright(c) 2006-2007, Ext JS, LLC.
23532  *
23533  * Originally Released Under LGPL - original licence link has changed is not relivant.
23534  *
23535  * Fork - LGPL
23536  * <script type="text/javascript">
23537  */
23538  
23539 // Base class for reading structured data from a data source.  This class is intended to be
23540 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23541
23542 /**
23543  * @class Roo.data.DataReader
23544  * Base class for reading structured data from a data source.  This class is intended to be
23545  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23546  */
23547
23548 Roo.data.DataReader = function(meta, recordType){
23549     
23550     this.meta = meta;
23551     
23552     this.recordType = recordType instanceof Array ? 
23553         Roo.data.Record.create(recordType) : recordType;
23554 };
23555
23556 Roo.data.DataReader.prototype = {
23557      /**
23558      * Create an empty record
23559      * @param {Object} data (optional) - overlay some values
23560      * @return {Roo.data.Record} record created.
23561      */
23562     newRow :  function(d) {
23563         var da =  {};
23564         this.recordType.prototype.fields.each(function(c) {
23565             switch( c.type) {
23566                 case 'int' : da[c.name] = 0; break;
23567                 case 'date' : da[c.name] = new Date(); break;
23568                 case 'float' : da[c.name] = 0.0; break;
23569                 case 'boolean' : da[c.name] = false; break;
23570                 default : da[c.name] = ""; break;
23571             }
23572             
23573         });
23574         return new this.recordType(Roo.apply(da, d));
23575     }
23576     
23577 };/*
23578  * Based on:
23579  * Ext JS Library 1.1.1
23580  * Copyright(c) 2006-2007, Ext JS, LLC.
23581  *
23582  * Originally Released Under LGPL - original licence link has changed is not relivant.
23583  *
23584  * Fork - LGPL
23585  * <script type="text/javascript">
23586  */
23587
23588 /**
23589  * @class Roo.data.DataProxy
23590  * @extends Roo.data.Observable
23591  * This class is an abstract base class for implementations which provide retrieval of
23592  * unformatted data objects.<br>
23593  * <p>
23594  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23595  * (of the appropriate type which knows how to parse the data object) to provide a block of
23596  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23597  * <p>
23598  * Custom implementations must implement the load method as described in
23599  * {@link Roo.data.HttpProxy#load}.
23600  */
23601 Roo.data.DataProxy = function(){
23602     this.addEvents({
23603         /**
23604          * @event beforeload
23605          * Fires before a network request is made to retrieve a data object.
23606          * @param {Object} This DataProxy object.
23607          * @param {Object} params The params parameter to the load function.
23608          */
23609         beforeload : true,
23610         /**
23611          * @event load
23612          * Fires before the load method's callback is called.
23613          * @param {Object} This DataProxy object.
23614          * @param {Object} o The data object.
23615          * @param {Object} arg The callback argument object passed to the load function.
23616          */
23617         load : true,
23618         /**
23619          * @event loadexception
23620          * Fires if an Exception occurs during data retrieval.
23621          * @param {Object} This DataProxy object.
23622          * @param {Object} o The data object.
23623          * @param {Object} arg The callback argument object passed to the load function.
23624          * @param {Object} e The Exception.
23625          */
23626         loadexception : true
23627     });
23628     Roo.data.DataProxy.superclass.constructor.call(this);
23629 };
23630
23631 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23632
23633     /**
23634      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23635      */
23636 /*
23637  * Based on:
23638  * Ext JS Library 1.1.1
23639  * Copyright(c) 2006-2007, Ext JS, LLC.
23640  *
23641  * Originally Released Under LGPL - original licence link has changed is not relivant.
23642  *
23643  * Fork - LGPL
23644  * <script type="text/javascript">
23645  */
23646 /**
23647  * @class Roo.data.MemoryProxy
23648  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23649  * to the Reader when its load method is called.
23650  * @constructor
23651  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23652  */
23653 Roo.data.MemoryProxy = function(data){
23654     if (data.data) {
23655         data = data.data;
23656     }
23657     Roo.data.MemoryProxy.superclass.constructor.call(this);
23658     this.data = data;
23659 };
23660
23661 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23662     
23663     /**
23664      * Load data from the requested source (in this case an in-memory
23665      * data object passed to the constructor), read the data object into
23666      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23667      * process that block using the passed callback.
23668      * @param {Object} params This parameter is not used by the MemoryProxy class.
23669      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23670      * object into a block of Roo.data.Records.
23671      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23672      * The function must be passed <ul>
23673      * <li>The Record block object</li>
23674      * <li>The "arg" argument from the load function</li>
23675      * <li>A boolean success indicator</li>
23676      * </ul>
23677      * @param {Object} scope The scope in which to call the callback
23678      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23679      */
23680     load : function(params, reader, callback, scope, arg){
23681         params = params || {};
23682         var result;
23683         try {
23684             result = reader.readRecords(this.data);
23685         }catch(e){
23686             this.fireEvent("loadexception", this, arg, null, e);
23687             callback.call(scope, null, arg, false);
23688             return;
23689         }
23690         callback.call(scope, result, arg, true);
23691     },
23692     
23693     // private
23694     update : function(params, records){
23695         
23696     }
23697 });/*
23698  * Based on:
23699  * Ext JS Library 1.1.1
23700  * Copyright(c) 2006-2007, Ext JS, LLC.
23701  *
23702  * Originally Released Under LGPL - original licence link has changed is not relivant.
23703  *
23704  * Fork - LGPL
23705  * <script type="text/javascript">
23706  */
23707 /**
23708  * @class Roo.data.HttpProxy
23709  * @extends Roo.data.DataProxy
23710  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23711  * configured to reference a certain URL.<br><br>
23712  * <p>
23713  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23714  * from which the running page was served.<br><br>
23715  * <p>
23716  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23717  * <p>
23718  * Be aware that to enable the browser to parse an XML document, the server must set
23719  * the Content-Type header in the HTTP response to "text/xml".
23720  * @constructor
23721  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23722  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23723  * will be used to make the request.
23724  */
23725 Roo.data.HttpProxy = function(conn){
23726     Roo.data.HttpProxy.superclass.constructor.call(this);
23727     // is conn a conn config or a real conn?
23728     this.conn = conn;
23729     this.useAjax = !conn || !conn.events;
23730   
23731 };
23732
23733 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23734     // thse are take from connection...
23735     
23736     /**
23737      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23738      */
23739     /**
23740      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23741      * extra parameters to each request made by this object. (defaults to undefined)
23742      */
23743     /**
23744      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23745      *  to each request made by this object. (defaults to undefined)
23746      */
23747     /**
23748      * @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)
23749      */
23750     /**
23751      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23752      */
23753      /**
23754      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23755      * @type Boolean
23756      */
23757   
23758
23759     /**
23760      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23761      * @type Boolean
23762      */
23763     /**
23764      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23765      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23766      * a finer-grained basis than the DataProxy events.
23767      */
23768     getConnection : function(){
23769         return this.useAjax ? Roo.Ajax : this.conn;
23770     },
23771
23772     /**
23773      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23774      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23775      * process that block using the passed callback.
23776      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23777      * for the request to the remote server.
23778      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23779      * object into a block of Roo.data.Records.
23780      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23781      * The function must be passed <ul>
23782      * <li>The Record block object</li>
23783      * <li>The "arg" argument from the load function</li>
23784      * <li>A boolean success indicator</li>
23785      * </ul>
23786      * @param {Object} scope The scope in which to call the callback
23787      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23788      */
23789     load : function(params, reader, callback, scope, arg){
23790         if(this.fireEvent("beforeload", this, params) !== false){
23791             var  o = {
23792                 params : params || {},
23793                 request: {
23794                     callback : callback,
23795                     scope : scope,
23796                     arg : arg
23797                 },
23798                 reader: reader,
23799                 callback : this.loadResponse,
23800                 scope: this
23801             };
23802             if(this.useAjax){
23803                 Roo.applyIf(o, this.conn);
23804                 if(this.activeRequest){
23805                     Roo.Ajax.abort(this.activeRequest);
23806                 }
23807                 this.activeRequest = Roo.Ajax.request(o);
23808             }else{
23809                 this.conn.request(o);
23810             }
23811         }else{
23812             callback.call(scope||this, null, arg, false);
23813         }
23814     },
23815
23816     // private
23817     loadResponse : function(o, success, response){
23818         delete this.activeRequest;
23819         if(!success){
23820             this.fireEvent("loadexception", this, o, response);
23821             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23822             return;
23823         }
23824         var result;
23825         try {
23826             result = o.reader.read(response);
23827         }catch(e){
23828             this.fireEvent("loadexception", this, o, response, e);
23829             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23830             return;
23831         }
23832         
23833         this.fireEvent("load", this, o, o.request.arg);
23834         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23835     },
23836
23837     // private
23838     update : function(dataSet){
23839
23840     },
23841
23842     // private
23843     updateResponse : function(dataSet){
23844
23845     }
23846 });/*
23847  * Based on:
23848  * Ext JS Library 1.1.1
23849  * Copyright(c) 2006-2007, Ext JS, LLC.
23850  *
23851  * Originally Released Under LGPL - original licence link has changed is not relivant.
23852  *
23853  * Fork - LGPL
23854  * <script type="text/javascript">
23855  */
23856
23857 /**
23858  * @class Roo.data.ScriptTagProxy
23859  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23860  * other than the originating domain of the running page.<br><br>
23861  * <p>
23862  * <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
23863  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23864  * <p>
23865  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23866  * source code that is used as the source inside a &lt;script> tag.<br><br>
23867  * <p>
23868  * In order for the browser to process the returned data, the server must wrap the data object
23869  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23870  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23871  * depending on whether the callback name was passed:
23872  * <p>
23873  * <pre><code>
23874 boolean scriptTag = false;
23875 String cb = request.getParameter("callback");
23876 if (cb != null) {
23877     scriptTag = true;
23878     response.setContentType("text/javascript");
23879 } else {
23880     response.setContentType("application/x-json");
23881 }
23882 Writer out = response.getWriter();
23883 if (scriptTag) {
23884     out.write(cb + "(");
23885 }
23886 out.print(dataBlock.toJsonString());
23887 if (scriptTag) {
23888     out.write(");");
23889 }
23890 </pre></code>
23891  *
23892  * @constructor
23893  * @param {Object} config A configuration object.
23894  */
23895 Roo.data.ScriptTagProxy = function(config){
23896     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23897     Roo.apply(this, config);
23898     this.head = document.getElementsByTagName("head")[0];
23899 };
23900
23901 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23902
23903 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23904     /**
23905      * @cfg {String} url The URL from which to request the data object.
23906      */
23907     /**
23908      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23909      */
23910     timeout : 30000,
23911     /**
23912      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23913      * the server the name of the callback function set up by the load call to process the returned data object.
23914      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23915      * javascript output which calls this named function passing the data object as its only parameter.
23916      */
23917     callbackParam : "callback",
23918     /**
23919      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23920      * name to the request.
23921      */
23922     nocache : true,
23923
23924     /**
23925      * Load data from the configured URL, read the data object into
23926      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23927      * process that block using the passed callback.
23928      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23929      * for the request to the remote server.
23930      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23931      * object into a block of Roo.data.Records.
23932      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23933      * The function must be passed <ul>
23934      * <li>The Record block object</li>
23935      * <li>The "arg" argument from the load function</li>
23936      * <li>A boolean success indicator</li>
23937      * </ul>
23938      * @param {Object} scope The scope in which to call the callback
23939      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23940      */
23941     load : function(params, reader, callback, scope, arg){
23942         if(this.fireEvent("beforeload", this, params) !== false){
23943
23944             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
23945
23946             var url = this.url;
23947             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
23948             if(this.nocache){
23949                 url += "&_dc=" + (new Date().getTime());
23950             }
23951             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
23952             var trans = {
23953                 id : transId,
23954                 cb : "stcCallback"+transId,
23955                 scriptId : "stcScript"+transId,
23956                 params : params,
23957                 arg : arg,
23958                 url : url,
23959                 callback : callback,
23960                 scope : scope,
23961                 reader : reader
23962             };
23963             var conn = this;
23964
23965             window[trans.cb] = function(o){
23966                 conn.handleResponse(o, trans);
23967             };
23968
23969             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
23970
23971             if(this.autoAbort !== false){
23972                 this.abort();
23973             }
23974
23975             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
23976
23977             var script = document.createElement("script");
23978             script.setAttribute("src", url);
23979             script.setAttribute("type", "text/javascript");
23980             script.setAttribute("id", trans.scriptId);
23981             this.head.appendChild(script);
23982
23983             this.trans = trans;
23984         }else{
23985             callback.call(scope||this, null, arg, false);
23986         }
23987     },
23988
23989     // private
23990     isLoading : function(){
23991         return this.trans ? true : false;
23992     },
23993
23994     /**
23995      * Abort the current server request.
23996      */
23997     abort : function(){
23998         if(this.isLoading()){
23999             this.destroyTrans(this.trans);
24000         }
24001     },
24002
24003     // private
24004     destroyTrans : function(trans, isLoaded){
24005         this.head.removeChild(document.getElementById(trans.scriptId));
24006         clearTimeout(trans.timeoutId);
24007         if(isLoaded){
24008             window[trans.cb] = undefined;
24009             try{
24010                 delete window[trans.cb];
24011             }catch(e){}
24012         }else{
24013             // if hasn't been loaded, wait for load to remove it to prevent script error
24014             window[trans.cb] = function(){
24015                 window[trans.cb] = undefined;
24016                 try{
24017                     delete window[trans.cb];
24018                 }catch(e){}
24019             };
24020         }
24021     },
24022
24023     // private
24024     handleResponse : function(o, trans){
24025         this.trans = false;
24026         this.destroyTrans(trans, true);
24027         var result;
24028         try {
24029             result = trans.reader.readRecords(o);
24030         }catch(e){
24031             this.fireEvent("loadexception", this, o, trans.arg, e);
24032             trans.callback.call(trans.scope||window, null, trans.arg, false);
24033             return;
24034         }
24035         this.fireEvent("load", this, o, trans.arg);
24036         trans.callback.call(trans.scope||window, result, trans.arg, true);
24037     },
24038
24039     // private
24040     handleFailure : function(trans){
24041         this.trans = false;
24042         this.destroyTrans(trans, false);
24043         this.fireEvent("loadexception", this, null, trans.arg);
24044         trans.callback.call(trans.scope||window, null, trans.arg, false);
24045     }
24046 });/*
24047  * Based on:
24048  * Ext JS Library 1.1.1
24049  * Copyright(c) 2006-2007, Ext JS, LLC.
24050  *
24051  * Originally Released Under LGPL - original licence link has changed is not relivant.
24052  *
24053  * Fork - LGPL
24054  * <script type="text/javascript">
24055  */
24056
24057 /**
24058  * @class Roo.data.JsonReader
24059  * @extends Roo.data.DataReader
24060  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24061  * based on mappings in a provided Roo.data.Record constructor.
24062  * 
24063  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24064  * in the reply previously. 
24065  * 
24066  * <p>
24067  * Example code:
24068  * <pre><code>
24069 var RecordDef = Roo.data.Record.create([
24070     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24071     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24072 ]);
24073 var myReader = new Roo.data.JsonReader({
24074     totalProperty: "results",    // The property which contains the total dataset size (optional)
24075     root: "rows",                // The property which contains an Array of row objects
24076     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24077 }, RecordDef);
24078 </code></pre>
24079  * <p>
24080  * This would consume a JSON file like this:
24081  * <pre><code>
24082 { 'results': 2, 'rows': [
24083     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24084     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24085 }
24086 </code></pre>
24087  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24088  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24089  * paged from the remote server.
24090  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24091  * @cfg {String} root name of the property which contains the Array of row objects.
24092  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24093  * @cfg {Array} fields Array of field definition objects
24094  * @constructor
24095  * Create a new JsonReader
24096  * @param {Object} meta Metadata configuration options
24097  * @param {Object} recordType Either an Array of field definition objects,
24098  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24099  */
24100 Roo.data.JsonReader = function(meta, recordType){
24101     
24102     meta = meta || {};
24103     // set some defaults:
24104     Roo.applyIf(meta, {
24105         totalProperty: 'total',
24106         successProperty : 'success',
24107         root : 'data',
24108         id : 'id'
24109     });
24110     
24111     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24112 };
24113 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24114     
24115     /**
24116      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24117      * Used by Store query builder to append _requestMeta to params.
24118      * 
24119      */
24120     metaFromRemote : false,
24121     /**
24122      * This method is only used by a DataProxy which has retrieved data from a remote server.
24123      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24124      * @return {Object} data A data block which is used by an Roo.data.Store object as
24125      * a cache of Roo.data.Records.
24126      */
24127     read : function(response){
24128         var json = response.responseText;
24129        
24130         var o = /* eval:var:o */ eval("("+json+")");
24131         if(!o) {
24132             throw {message: "JsonReader.read: Json object not found"};
24133         }
24134         
24135         if(o.metaData){
24136             
24137             delete this.ef;
24138             this.metaFromRemote = true;
24139             this.meta = o.metaData;
24140             this.recordType = Roo.data.Record.create(o.metaData.fields);
24141             this.onMetaChange(this.meta, this.recordType, o);
24142         }
24143         return this.readRecords(o);
24144     },
24145
24146     // private function a store will implement
24147     onMetaChange : function(meta, recordType, o){
24148
24149     },
24150
24151     /**
24152          * @ignore
24153          */
24154     simpleAccess: function(obj, subsc) {
24155         return obj[subsc];
24156     },
24157
24158         /**
24159          * @ignore
24160          */
24161     getJsonAccessor: function(){
24162         var re = /[\[\.]/;
24163         return function(expr) {
24164             try {
24165                 return(re.test(expr))
24166                     ? new Function("obj", "return obj." + expr)
24167                     : function(obj){
24168                         return obj[expr];
24169                     };
24170             } catch(e){}
24171             return Roo.emptyFn;
24172         };
24173     }(),
24174
24175     /**
24176      * Create a data block containing Roo.data.Records from an XML document.
24177      * @param {Object} o An object which contains an Array of row objects in the property specified
24178      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24179      * which contains the total size of the dataset.
24180      * @return {Object} data A data block which is used by an Roo.data.Store object as
24181      * a cache of Roo.data.Records.
24182      */
24183     readRecords : function(o){
24184         /**
24185          * After any data loads, the raw JSON data is available for further custom processing.
24186          * @type Object
24187          */
24188         this.o = o;
24189         var s = this.meta, Record = this.recordType,
24190             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24191
24192 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24193         if (!this.ef) {
24194             if(s.totalProperty) {
24195                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24196                 }
24197                 if(s.successProperty) {
24198                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24199                 }
24200                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24201                 if (s.id) {
24202                         var g = this.getJsonAccessor(s.id);
24203                         this.getId = function(rec) {
24204                                 var r = g(rec);  
24205                                 return (r === undefined || r === "") ? null : r;
24206                         };
24207                 } else {
24208                         this.getId = function(){return null;};
24209                 }
24210             this.ef = [];
24211             for(var jj = 0; jj < fl; jj++){
24212                 f = fi[jj];
24213                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24214                 this.ef[jj] = this.getJsonAccessor(map);
24215             }
24216         }
24217
24218         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24219         if(s.totalProperty){
24220             var vt = parseInt(this.getTotal(o), 10);
24221             if(!isNaN(vt)){
24222                 totalRecords = vt;
24223             }
24224         }
24225         if(s.successProperty){
24226             var vs = this.getSuccess(o);
24227             if(vs === false || vs === 'false'){
24228                 success = false;
24229             }
24230         }
24231         var records = [];
24232         for(var i = 0; i < c; i++){
24233                 var n = root[i];
24234             var values = {};
24235             var id = this.getId(n);
24236             for(var j = 0; j < fl; j++){
24237                 f = fi[j];
24238             var v = this.ef[j](n);
24239             if (!f.convert) {
24240                 Roo.log('missing convert for ' + f.name);
24241                 Roo.log(f);
24242                 continue;
24243             }
24244             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24245             }
24246             var record = new Record(values, id);
24247             record.json = n;
24248             records[i] = record;
24249         }
24250         return {
24251             raw : o,
24252             success : success,
24253             records : records,
24254             totalRecords : totalRecords
24255         };
24256     }
24257 });/*
24258  * Based on:
24259  * Ext JS Library 1.1.1
24260  * Copyright(c) 2006-2007, Ext JS, LLC.
24261  *
24262  * Originally Released Under LGPL - original licence link has changed is not relivant.
24263  *
24264  * Fork - LGPL
24265  * <script type="text/javascript">
24266  */
24267
24268 /**
24269  * @class Roo.data.XmlReader
24270  * @extends Roo.data.DataReader
24271  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24272  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24273  * <p>
24274  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24275  * header in the HTTP response must be set to "text/xml".</em>
24276  * <p>
24277  * Example code:
24278  * <pre><code>
24279 var RecordDef = Roo.data.Record.create([
24280    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24281    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24282 ]);
24283 var myReader = new Roo.data.XmlReader({
24284    totalRecords: "results", // The element which contains the total dataset size (optional)
24285    record: "row",           // The repeated element which contains row information
24286    id: "id"                 // The element within the row that provides an ID for the record (optional)
24287 }, RecordDef);
24288 </code></pre>
24289  * <p>
24290  * This would consume an XML file like this:
24291  * <pre><code>
24292 &lt;?xml?>
24293 &lt;dataset>
24294  &lt;results>2&lt;/results>
24295  &lt;row>
24296    &lt;id>1&lt;/id>
24297    &lt;name>Bill&lt;/name>
24298    &lt;occupation>Gardener&lt;/occupation>
24299  &lt;/row>
24300  &lt;row>
24301    &lt;id>2&lt;/id>
24302    &lt;name>Ben&lt;/name>
24303    &lt;occupation>Horticulturalist&lt;/occupation>
24304  &lt;/row>
24305 &lt;/dataset>
24306 </code></pre>
24307  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24308  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24309  * paged from the remote server.
24310  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24311  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24312  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24313  * a record identifier value.
24314  * @constructor
24315  * Create a new XmlReader
24316  * @param {Object} meta Metadata configuration options
24317  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24318  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24319  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24320  */
24321 Roo.data.XmlReader = function(meta, recordType){
24322     meta = meta || {};
24323     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24324 };
24325 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24326     /**
24327      * This method is only used by a DataProxy which has retrieved data from a remote server.
24328          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24329          * to contain a method called 'responseXML' that returns an XML document object.
24330      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24331      * a cache of Roo.data.Records.
24332      */
24333     read : function(response){
24334         var doc = response.responseXML;
24335         if(!doc) {
24336             throw {message: "XmlReader.read: XML Document not available"};
24337         }
24338         return this.readRecords(doc);
24339     },
24340
24341     /**
24342      * Create a data block containing Roo.data.Records from an XML document.
24343          * @param {Object} doc A parsed XML document.
24344      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24345      * a cache of Roo.data.Records.
24346      */
24347     readRecords : function(doc){
24348         /**
24349          * After any data loads/reads, the raw XML Document is available for further custom processing.
24350          * @type XMLDocument
24351          */
24352         this.xmlData = doc;
24353         var root = doc.documentElement || doc;
24354         var q = Roo.DomQuery;
24355         var recordType = this.recordType, fields = recordType.prototype.fields;
24356         var sid = this.meta.id;
24357         var totalRecords = 0, success = true;
24358         if(this.meta.totalRecords){
24359             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24360         }
24361         
24362         if(this.meta.success){
24363             var sv = q.selectValue(this.meta.success, root, true);
24364             success = sv !== false && sv !== 'false';
24365         }
24366         var records = [];
24367         var ns = q.select(this.meta.record, root);
24368         for(var i = 0, len = ns.length; i < len; i++) {
24369                 var n = ns[i];
24370                 var values = {};
24371                 var id = sid ? q.selectValue(sid, n) : undefined;
24372                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24373                     var f = fields.items[j];
24374                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24375                     v = f.convert(v);
24376                     values[f.name] = v;
24377                 }
24378                 var record = new recordType(values, id);
24379                 record.node = n;
24380                 records[records.length] = record;
24381             }
24382
24383             return {
24384                 success : success,
24385                 records : records,
24386                 totalRecords : totalRecords || records.length
24387             };
24388     }
24389 });/*
24390  * Based on:
24391  * Ext JS Library 1.1.1
24392  * Copyright(c) 2006-2007, Ext JS, LLC.
24393  *
24394  * Originally Released Under LGPL - original licence link has changed is not relivant.
24395  *
24396  * Fork - LGPL
24397  * <script type="text/javascript">
24398  */
24399
24400 /**
24401  * @class Roo.data.ArrayReader
24402  * @extends Roo.data.DataReader
24403  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24404  * Each element of that Array represents a row of data fields. The
24405  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24406  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24407  * <p>
24408  * Example code:.
24409  * <pre><code>
24410 var RecordDef = Roo.data.Record.create([
24411     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24412     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24413 ]);
24414 var myReader = new Roo.data.ArrayReader({
24415     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24416 }, RecordDef);
24417 </code></pre>
24418  * <p>
24419  * This would consume an Array like this:
24420  * <pre><code>
24421 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24422   </code></pre>
24423  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24424  * @constructor
24425  * Create a new JsonReader
24426  * @param {Object} meta Metadata configuration options.
24427  * @param {Object} recordType Either an Array of field definition objects
24428  * as specified to {@link Roo.data.Record#create},
24429  * or an {@link Roo.data.Record} object
24430  * created using {@link Roo.data.Record#create}.
24431  */
24432 Roo.data.ArrayReader = function(meta, recordType){
24433     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24434 };
24435
24436 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24437     /**
24438      * Create a data block containing Roo.data.Records from an XML document.
24439      * @param {Object} o An Array of row objects which represents the dataset.
24440      * @return {Object} data A data block which is used by an Roo.data.Store object as
24441      * a cache of Roo.data.Records.
24442      */
24443     readRecords : function(o){
24444         var sid = this.meta ? this.meta.id : null;
24445         var recordType = this.recordType, fields = recordType.prototype.fields;
24446         var records = [];
24447         var root = o;
24448             for(var i = 0; i < root.length; i++){
24449                     var n = root[i];
24450                 var values = {};
24451                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24452                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24453                 var f = fields.items[j];
24454                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24455                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24456                 v = f.convert(v);
24457                 values[f.name] = v;
24458             }
24459                 var record = new recordType(values, id);
24460                 record.json = n;
24461                 records[records.length] = record;
24462             }
24463             return {
24464                 records : records,
24465                 totalRecords : records.length
24466             };
24467     }
24468 });/*
24469  * Based on:
24470  * Ext JS Library 1.1.1
24471  * Copyright(c) 2006-2007, Ext JS, LLC.
24472  *
24473  * Originally Released Under LGPL - original licence link has changed is not relivant.
24474  *
24475  * Fork - LGPL
24476  * <script type="text/javascript">
24477  */
24478
24479
24480 /**
24481  * @class Roo.data.Tree
24482  * @extends Roo.util.Observable
24483  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24484  * in the tree have most standard DOM functionality.
24485  * @constructor
24486  * @param {Node} root (optional) The root node
24487  */
24488 Roo.data.Tree = function(root){
24489    this.nodeHash = {};
24490    /**
24491     * The root node for this tree
24492     * @type Node
24493     */
24494    this.root = null;
24495    if(root){
24496        this.setRootNode(root);
24497    }
24498    this.addEvents({
24499        /**
24500         * @event append
24501         * Fires when a new child node is appended to a node in this tree.
24502         * @param {Tree} tree The owner tree
24503         * @param {Node} parent The parent node
24504         * @param {Node} node The newly appended node
24505         * @param {Number} index The index of the newly appended node
24506         */
24507        "append" : true,
24508        /**
24509         * @event remove
24510         * Fires when a child node is removed from a node in this tree.
24511         * @param {Tree} tree The owner tree
24512         * @param {Node} parent The parent node
24513         * @param {Node} node The child node removed
24514         */
24515        "remove" : true,
24516        /**
24517         * @event move
24518         * Fires when a node is moved to a new location in the tree
24519         * @param {Tree} tree The owner tree
24520         * @param {Node} node The node moved
24521         * @param {Node} oldParent The old parent of this node
24522         * @param {Node} newParent The new parent of this node
24523         * @param {Number} index The index it was moved to
24524         */
24525        "move" : true,
24526        /**
24527         * @event insert
24528         * Fires when a new child node is inserted in a node in this tree.
24529         * @param {Tree} tree The owner tree
24530         * @param {Node} parent The parent node
24531         * @param {Node} node The child node inserted
24532         * @param {Node} refNode The child node the node was inserted before
24533         */
24534        "insert" : true,
24535        /**
24536         * @event beforeappend
24537         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24538         * @param {Tree} tree The owner tree
24539         * @param {Node} parent The parent node
24540         * @param {Node} node The child node to be appended
24541         */
24542        "beforeappend" : true,
24543        /**
24544         * @event beforeremove
24545         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24546         * @param {Tree} tree The owner tree
24547         * @param {Node} parent The parent node
24548         * @param {Node} node The child node to be removed
24549         */
24550        "beforeremove" : true,
24551        /**
24552         * @event beforemove
24553         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24554         * @param {Tree} tree The owner tree
24555         * @param {Node} node The node being moved
24556         * @param {Node} oldParent The parent of the node
24557         * @param {Node} newParent The new parent the node is moving to
24558         * @param {Number} index The index it is being moved to
24559         */
24560        "beforemove" : true,
24561        /**
24562         * @event beforeinsert
24563         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24564         * @param {Tree} tree The owner tree
24565         * @param {Node} parent The parent node
24566         * @param {Node} node The child node to be inserted
24567         * @param {Node} refNode The child node the node is being inserted before
24568         */
24569        "beforeinsert" : true
24570    });
24571
24572     Roo.data.Tree.superclass.constructor.call(this);
24573 };
24574
24575 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24576     pathSeparator: "/",
24577
24578     proxyNodeEvent : function(){
24579         return this.fireEvent.apply(this, arguments);
24580     },
24581
24582     /**
24583      * Returns the root node for this tree.
24584      * @return {Node}
24585      */
24586     getRootNode : function(){
24587         return this.root;
24588     },
24589
24590     /**
24591      * Sets the root node for this tree.
24592      * @param {Node} node
24593      * @return {Node}
24594      */
24595     setRootNode : function(node){
24596         this.root = node;
24597         node.ownerTree = this;
24598         node.isRoot = true;
24599         this.registerNode(node);
24600         return node;
24601     },
24602
24603     /**
24604      * Gets a node in this tree by its id.
24605      * @param {String} id
24606      * @return {Node}
24607      */
24608     getNodeById : function(id){
24609         return this.nodeHash[id];
24610     },
24611
24612     registerNode : function(node){
24613         this.nodeHash[node.id] = node;
24614     },
24615
24616     unregisterNode : function(node){
24617         delete this.nodeHash[node.id];
24618     },
24619
24620     toString : function(){
24621         return "[Tree"+(this.id?" "+this.id:"")+"]";
24622     }
24623 });
24624
24625 /**
24626  * @class Roo.data.Node
24627  * @extends Roo.util.Observable
24628  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24629  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24630  * @constructor
24631  * @param {Object} attributes The attributes/config for the node
24632  */
24633 Roo.data.Node = function(attributes){
24634     /**
24635      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24636      * @type {Object}
24637      */
24638     this.attributes = attributes || {};
24639     this.leaf = this.attributes.leaf;
24640     /**
24641      * The node id. @type String
24642      */
24643     this.id = this.attributes.id;
24644     if(!this.id){
24645         this.id = Roo.id(null, "ynode-");
24646         this.attributes.id = this.id;
24647     }
24648      
24649     
24650     /**
24651      * All child nodes of this node. @type Array
24652      */
24653     this.childNodes = [];
24654     if(!this.childNodes.indexOf){ // indexOf is a must
24655         this.childNodes.indexOf = function(o){
24656             for(var i = 0, len = this.length; i < len; i++){
24657                 if(this[i] == o) {
24658                     return i;
24659                 }
24660             }
24661             return -1;
24662         };
24663     }
24664     /**
24665      * The parent node for this node. @type Node
24666      */
24667     this.parentNode = null;
24668     /**
24669      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24670      */
24671     this.firstChild = null;
24672     /**
24673      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24674      */
24675     this.lastChild = null;
24676     /**
24677      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24678      */
24679     this.previousSibling = null;
24680     /**
24681      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24682      */
24683     this.nextSibling = null;
24684
24685     this.addEvents({
24686        /**
24687         * @event append
24688         * Fires when a new child node is appended
24689         * @param {Tree} tree The owner tree
24690         * @param {Node} this This node
24691         * @param {Node} node The newly appended node
24692         * @param {Number} index The index of the newly appended node
24693         */
24694        "append" : true,
24695        /**
24696         * @event remove
24697         * Fires when a child node is removed
24698         * @param {Tree} tree The owner tree
24699         * @param {Node} this This node
24700         * @param {Node} node The removed node
24701         */
24702        "remove" : true,
24703        /**
24704         * @event move
24705         * Fires when this node is moved to a new location in the tree
24706         * @param {Tree} tree The owner tree
24707         * @param {Node} this This node
24708         * @param {Node} oldParent The old parent of this node
24709         * @param {Node} newParent The new parent of this node
24710         * @param {Number} index The index it was moved to
24711         */
24712        "move" : true,
24713        /**
24714         * @event insert
24715         * Fires when a new child node is inserted.
24716         * @param {Tree} tree The owner tree
24717         * @param {Node} this This node
24718         * @param {Node} node The child node inserted
24719         * @param {Node} refNode The child node the node was inserted before
24720         */
24721        "insert" : true,
24722        /**
24723         * @event beforeappend
24724         * Fires before a new child is appended, return false to cancel the append.
24725         * @param {Tree} tree The owner tree
24726         * @param {Node} this This node
24727         * @param {Node} node The child node to be appended
24728         */
24729        "beforeappend" : true,
24730        /**
24731         * @event beforeremove
24732         * Fires before a child is removed, return false to cancel the remove.
24733         * @param {Tree} tree The owner tree
24734         * @param {Node} this This node
24735         * @param {Node} node The child node to be removed
24736         */
24737        "beforeremove" : true,
24738        /**
24739         * @event beforemove
24740         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24741         * @param {Tree} tree The owner tree
24742         * @param {Node} this This node
24743         * @param {Node} oldParent The parent of this node
24744         * @param {Node} newParent The new parent this node is moving to
24745         * @param {Number} index The index it is being moved to
24746         */
24747        "beforemove" : true,
24748        /**
24749         * @event beforeinsert
24750         * Fires before a new child is inserted, return false to cancel the insert.
24751         * @param {Tree} tree The owner tree
24752         * @param {Node} this This node
24753         * @param {Node} node The child node to be inserted
24754         * @param {Node} refNode The child node the node is being inserted before
24755         */
24756        "beforeinsert" : true
24757    });
24758     this.listeners = this.attributes.listeners;
24759     Roo.data.Node.superclass.constructor.call(this);
24760 };
24761
24762 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24763     fireEvent : function(evtName){
24764         // first do standard event for this node
24765         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24766             return false;
24767         }
24768         // then bubble it up to the tree if the event wasn't cancelled
24769         var ot = this.getOwnerTree();
24770         if(ot){
24771             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24772                 return false;
24773             }
24774         }
24775         return true;
24776     },
24777
24778     /**
24779      * Returns true if this node is a leaf
24780      * @return {Boolean}
24781      */
24782     isLeaf : function(){
24783         return this.leaf === true;
24784     },
24785
24786     // private
24787     setFirstChild : function(node){
24788         this.firstChild = node;
24789     },
24790
24791     //private
24792     setLastChild : function(node){
24793         this.lastChild = node;
24794     },
24795
24796
24797     /**
24798      * Returns true if this node is the last child of its parent
24799      * @return {Boolean}
24800      */
24801     isLast : function(){
24802        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24803     },
24804
24805     /**
24806      * Returns true if this node is the first child of its parent
24807      * @return {Boolean}
24808      */
24809     isFirst : function(){
24810        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24811     },
24812
24813     hasChildNodes : function(){
24814         return !this.isLeaf() && this.childNodes.length > 0;
24815     },
24816
24817     /**
24818      * Insert node(s) as the last child node of this node.
24819      * @param {Node/Array} node The node or Array of nodes to append
24820      * @return {Node} The appended node if single append, or null if an array was passed
24821      */
24822     appendChild : function(node){
24823         var multi = false;
24824         if(node instanceof Array){
24825             multi = node;
24826         }else if(arguments.length > 1){
24827             multi = arguments;
24828         }
24829         // if passed an array or multiple args do them one by one
24830         if(multi){
24831             for(var i = 0, len = multi.length; i < len; i++) {
24832                 this.appendChild(multi[i]);
24833             }
24834         }else{
24835             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24836                 return false;
24837             }
24838             var index = this.childNodes.length;
24839             var oldParent = node.parentNode;
24840             // it's a move, make sure we move it cleanly
24841             if(oldParent){
24842                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24843                     return false;
24844                 }
24845                 oldParent.removeChild(node);
24846             }
24847             index = this.childNodes.length;
24848             if(index == 0){
24849                 this.setFirstChild(node);
24850             }
24851             this.childNodes.push(node);
24852             node.parentNode = this;
24853             var ps = this.childNodes[index-1];
24854             if(ps){
24855                 node.previousSibling = ps;
24856                 ps.nextSibling = node;
24857             }else{
24858                 node.previousSibling = null;
24859             }
24860             node.nextSibling = null;
24861             this.setLastChild(node);
24862             node.setOwnerTree(this.getOwnerTree());
24863             this.fireEvent("append", this.ownerTree, this, node, index);
24864             if(oldParent){
24865                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24866             }
24867             return node;
24868         }
24869     },
24870
24871     /**
24872      * Removes a child node from this node.
24873      * @param {Node} node The node to remove
24874      * @return {Node} The removed node
24875      */
24876     removeChild : function(node){
24877         var index = this.childNodes.indexOf(node);
24878         if(index == -1){
24879             return false;
24880         }
24881         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24882             return false;
24883         }
24884
24885         // remove it from childNodes collection
24886         this.childNodes.splice(index, 1);
24887
24888         // update siblings
24889         if(node.previousSibling){
24890             node.previousSibling.nextSibling = node.nextSibling;
24891         }
24892         if(node.nextSibling){
24893             node.nextSibling.previousSibling = node.previousSibling;
24894         }
24895
24896         // update child refs
24897         if(this.firstChild == node){
24898             this.setFirstChild(node.nextSibling);
24899         }
24900         if(this.lastChild == node){
24901             this.setLastChild(node.previousSibling);
24902         }
24903
24904         node.setOwnerTree(null);
24905         // clear any references from the node
24906         node.parentNode = null;
24907         node.previousSibling = null;
24908         node.nextSibling = null;
24909         this.fireEvent("remove", this.ownerTree, this, node);
24910         return node;
24911     },
24912
24913     /**
24914      * Inserts the first node before the second node in this nodes childNodes collection.
24915      * @param {Node} node The node to insert
24916      * @param {Node} refNode The node to insert before (if null the node is appended)
24917      * @return {Node} The inserted node
24918      */
24919     insertBefore : function(node, refNode){
24920         if(!refNode){ // like standard Dom, refNode can be null for append
24921             return this.appendChild(node);
24922         }
24923         // nothing to do
24924         if(node == refNode){
24925             return false;
24926         }
24927
24928         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24929             return false;
24930         }
24931         var index = this.childNodes.indexOf(refNode);
24932         var oldParent = node.parentNode;
24933         var refIndex = index;
24934
24935         // when moving internally, indexes will change after remove
24936         if(oldParent == this && this.childNodes.indexOf(node) < index){
24937             refIndex--;
24938         }
24939
24940         // it's a move, make sure we move it cleanly
24941         if(oldParent){
24942             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24943                 return false;
24944             }
24945             oldParent.removeChild(node);
24946         }
24947         if(refIndex == 0){
24948             this.setFirstChild(node);
24949         }
24950         this.childNodes.splice(refIndex, 0, node);
24951         node.parentNode = this;
24952         var ps = this.childNodes[refIndex-1];
24953         if(ps){
24954             node.previousSibling = ps;
24955             ps.nextSibling = node;
24956         }else{
24957             node.previousSibling = null;
24958         }
24959         node.nextSibling = refNode;
24960         refNode.previousSibling = node;
24961         node.setOwnerTree(this.getOwnerTree());
24962         this.fireEvent("insert", this.ownerTree, this, node, refNode);
24963         if(oldParent){
24964             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
24965         }
24966         return node;
24967     },
24968
24969     /**
24970      * Returns the child node at the specified index.
24971      * @param {Number} index
24972      * @return {Node}
24973      */
24974     item : function(index){
24975         return this.childNodes[index];
24976     },
24977
24978     /**
24979      * Replaces one child node in this node with another.
24980      * @param {Node} newChild The replacement node
24981      * @param {Node} oldChild The node to replace
24982      * @return {Node} The replaced node
24983      */
24984     replaceChild : function(newChild, oldChild){
24985         this.insertBefore(newChild, oldChild);
24986         this.removeChild(oldChild);
24987         return oldChild;
24988     },
24989
24990     /**
24991      * Returns the index of a child node
24992      * @param {Node} node
24993      * @return {Number} The index of the node or -1 if it was not found
24994      */
24995     indexOf : function(child){
24996         return this.childNodes.indexOf(child);
24997     },
24998
24999     /**
25000      * Returns the tree this node is in.
25001      * @return {Tree}
25002      */
25003     getOwnerTree : function(){
25004         // if it doesn't have one, look for one
25005         if(!this.ownerTree){
25006             var p = this;
25007             while(p){
25008                 if(p.ownerTree){
25009                     this.ownerTree = p.ownerTree;
25010                     break;
25011                 }
25012                 p = p.parentNode;
25013             }
25014         }
25015         return this.ownerTree;
25016     },
25017
25018     /**
25019      * Returns depth of this node (the root node has a depth of 0)
25020      * @return {Number}
25021      */
25022     getDepth : function(){
25023         var depth = 0;
25024         var p = this;
25025         while(p.parentNode){
25026             ++depth;
25027             p = p.parentNode;
25028         }
25029         return depth;
25030     },
25031
25032     // private
25033     setOwnerTree : function(tree){
25034         // if it's move, we need to update everyone
25035         if(tree != this.ownerTree){
25036             if(this.ownerTree){
25037                 this.ownerTree.unregisterNode(this);
25038             }
25039             this.ownerTree = tree;
25040             var cs = this.childNodes;
25041             for(var i = 0, len = cs.length; i < len; i++) {
25042                 cs[i].setOwnerTree(tree);
25043             }
25044             if(tree){
25045                 tree.registerNode(this);
25046             }
25047         }
25048     },
25049
25050     /**
25051      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25052      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25053      * @return {String} The path
25054      */
25055     getPath : function(attr){
25056         attr = attr || "id";
25057         var p = this.parentNode;
25058         var b = [this.attributes[attr]];
25059         while(p){
25060             b.unshift(p.attributes[attr]);
25061             p = p.parentNode;
25062         }
25063         var sep = this.getOwnerTree().pathSeparator;
25064         return sep + b.join(sep);
25065     },
25066
25067     /**
25068      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25069      * function call will be the scope provided or the current node. The arguments to the function
25070      * will be the args provided or the current node. If the function returns false at any point,
25071      * the bubble is stopped.
25072      * @param {Function} fn The function to call
25073      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25074      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25075      */
25076     bubble : function(fn, scope, args){
25077         var p = this;
25078         while(p){
25079             if(fn.call(scope || p, args || p) === false){
25080                 break;
25081             }
25082             p = p.parentNode;
25083         }
25084     },
25085
25086     /**
25087      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25088      * function call will be the scope provided or the current node. The arguments to the function
25089      * will be the args provided or the current node. If the function returns false at any point,
25090      * the cascade is stopped on that branch.
25091      * @param {Function} fn The function to call
25092      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25093      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25094      */
25095     cascade : function(fn, scope, args){
25096         if(fn.call(scope || this, args || this) !== false){
25097             var cs = this.childNodes;
25098             for(var i = 0, len = cs.length; i < len; i++) {
25099                 cs[i].cascade(fn, scope, args);
25100             }
25101         }
25102     },
25103
25104     /**
25105      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25106      * function call will be the scope provided or the current node. The arguments to the function
25107      * will be the args provided or the current node. If the function returns false at any point,
25108      * the iteration stops.
25109      * @param {Function} fn The function to call
25110      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25111      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25112      */
25113     eachChild : function(fn, scope, args){
25114         var cs = this.childNodes;
25115         for(var i = 0, len = cs.length; i < len; i++) {
25116                 if(fn.call(scope || this, args || cs[i]) === false){
25117                     break;
25118                 }
25119         }
25120     },
25121
25122     /**
25123      * Finds the first child that has the attribute with the specified value.
25124      * @param {String} attribute The attribute name
25125      * @param {Mixed} value The value to search for
25126      * @return {Node} The found child or null if none was found
25127      */
25128     findChild : function(attribute, value){
25129         var cs = this.childNodes;
25130         for(var i = 0, len = cs.length; i < len; i++) {
25131                 if(cs[i].attributes[attribute] == value){
25132                     return cs[i];
25133                 }
25134         }
25135         return null;
25136     },
25137
25138     /**
25139      * Finds the first child by a custom function. The child matches if the function passed
25140      * returns true.
25141      * @param {Function} fn
25142      * @param {Object} scope (optional)
25143      * @return {Node} The found child or null if none was found
25144      */
25145     findChildBy : function(fn, scope){
25146         var cs = this.childNodes;
25147         for(var i = 0, len = cs.length; i < len; i++) {
25148                 if(fn.call(scope||cs[i], cs[i]) === true){
25149                     return cs[i];
25150                 }
25151         }
25152         return null;
25153     },
25154
25155     /**
25156      * Sorts this nodes children using the supplied sort function
25157      * @param {Function} fn
25158      * @param {Object} scope (optional)
25159      */
25160     sort : function(fn, scope){
25161         var cs = this.childNodes;
25162         var len = cs.length;
25163         if(len > 0){
25164             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25165             cs.sort(sortFn);
25166             for(var i = 0; i < len; i++){
25167                 var n = cs[i];
25168                 n.previousSibling = cs[i-1];
25169                 n.nextSibling = cs[i+1];
25170                 if(i == 0){
25171                     this.setFirstChild(n);
25172                 }
25173                 if(i == len-1){
25174                     this.setLastChild(n);
25175                 }
25176             }
25177         }
25178     },
25179
25180     /**
25181      * Returns true if this node is an ancestor (at any point) of the passed node.
25182      * @param {Node} node
25183      * @return {Boolean}
25184      */
25185     contains : function(node){
25186         return node.isAncestor(this);
25187     },
25188
25189     /**
25190      * Returns true if the passed node is an ancestor (at any point) of this node.
25191      * @param {Node} node
25192      * @return {Boolean}
25193      */
25194     isAncestor : function(node){
25195         var p = this.parentNode;
25196         while(p){
25197             if(p == node){
25198                 return true;
25199             }
25200             p = p.parentNode;
25201         }
25202         return false;
25203     },
25204
25205     toString : function(){
25206         return "[Node"+(this.id?" "+this.id:"")+"]";
25207     }
25208 });/*
25209  * Based on:
25210  * Ext JS Library 1.1.1
25211  * Copyright(c) 2006-2007, Ext JS, LLC.
25212  *
25213  * Originally Released Under LGPL - original licence link has changed is not relivant.
25214  *
25215  * Fork - LGPL
25216  * <script type="text/javascript">
25217  */
25218  (function(){ 
25219 /**
25220  * @class Roo.Layer
25221  * @extends Roo.Element
25222  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25223  * automatic maintaining of shadow/shim positions.
25224  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25225  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25226  * you can pass a string with a CSS class name. False turns off the shadow.
25227  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25228  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25229  * @cfg {String} cls CSS class to add to the element
25230  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25231  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25232  * @constructor
25233  * @param {Object} config An object with config options.
25234  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25235  */
25236
25237 Roo.Layer = function(config, existingEl){
25238     config = config || {};
25239     var dh = Roo.DomHelper;
25240     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25241     if(existingEl){
25242         this.dom = Roo.getDom(existingEl);
25243     }
25244     if(!this.dom){
25245         var o = config.dh || {tag: "div", cls: "x-layer"};
25246         this.dom = dh.append(pel, o);
25247     }
25248     if(config.cls){
25249         this.addClass(config.cls);
25250     }
25251     this.constrain = config.constrain !== false;
25252     this.visibilityMode = Roo.Element.VISIBILITY;
25253     if(config.id){
25254         this.id = this.dom.id = config.id;
25255     }else{
25256         this.id = Roo.id(this.dom);
25257     }
25258     this.zindex = config.zindex || this.getZIndex();
25259     this.position("absolute", this.zindex);
25260     if(config.shadow){
25261         this.shadowOffset = config.shadowOffset || 4;
25262         this.shadow = new Roo.Shadow({
25263             offset : this.shadowOffset,
25264             mode : config.shadow
25265         });
25266     }else{
25267         this.shadowOffset = 0;
25268     }
25269     this.useShim = config.shim !== false && Roo.useShims;
25270     this.useDisplay = config.useDisplay;
25271     this.hide();
25272 };
25273
25274 var supr = Roo.Element.prototype;
25275
25276 // shims are shared among layer to keep from having 100 iframes
25277 var shims = [];
25278
25279 Roo.extend(Roo.Layer, Roo.Element, {
25280
25281     getZIndex : function(){
25282         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25283     },
25284
25285     getShim : function(){
25286         if(!this.useShim){
25287             return null;
25288         }
25289         if(this.shim){
25290             return this.shim;
25291         }
25292         var shim = shims.shift();
25293         if(!shim){
25294             shim = this.createShim();
25295             shim.enableDisplayMode('block');
25296             shim.dom.style.display = 'none';
25297             shim.dom.style.visibility = 'visible';
25298         }
25299         var pn = this.dom.parentNode;
25300         if(shim.dom.parentNode != pn){
25301             pn.insertBefore(shim.dom, this.dom);
25302         }
25303         shim.setStyle('z-index', this.getZIndex()-2);
25304         this.shim = shim;
25305         return shim;
25306     },
25307
25308     hideShim : function(){
25309         if(this.shim){
25310             this.shim.setDisplayed(false);
25311             shims.push(this.shim);
25312             delete this.shim;
25313         }
25314     },
25315
25316     disableShadow : function(){
25317         if(this.shadow){
25318             this.shadowDisabled = true;
25319             this.shadow.hide();
25320             this.lastShadowOffset = this.shadowOffset;
25321             this.shadowOffset = 0;
25322         }
25323     },
25324
25325     enableShadow : function(show){
25326         if(this.shadow){
25327             this.shadowDisabled = false;
25328             this.shadowOffset = this.lastShadowOffset;
25329             delete this.lastShadowOffset;
25330             if(show){
25331                 this.sync(true);
25332             }
25333         }
25334     },
25335
25336     // private
25337     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25338     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25339     sync : function(doShow){
25340         var sw = this.shadow;
25341         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25342             var sh = this.getShim();
25343
25344             var w = this.getWidth(),
25345                 h = this.getHeight();
25346
25347             var l = this.getLeft(true),
25348                 t = this.getTop(true);
25349
25350             if(sw && !this.shadowDisabled){
25351                 if(doShow && !sw.isVisible()){
25352                     sw.show(this);
25353                 }else{
25354                     sw.realign(l, t, w, h);
25355                 }
25356                 if(sh){
25357                     if(doShow){
25358                        sh.show();
25359                     }
25360                     // fit the shim behind the shadow, so it is shimmed too
25361                     var a = sw.adjusts, s = sh.dom.style;
25362                     s.left = (Math.min(l, l+a.l))+"px";
25363                     s.top = (Math.min(t, t+a.t))+"px";
25364                     s.width = (w+a.w)+"px";
25365                     s.height = (h+a.h)+"px";
25366                 }
25367             }else if(sh){
25368                 if(doShow){
25369                    sh.show();
25370                 }
25371                 sh.setSize(w, h);
25372                 sh.setLeftTop(l, t);
25373             }
25374             
25375         }
25376     },
25377
25378     // private
25379     destroy : function(){
25380         this.hideShim();
25381         if(this.shadow){
25382             this.shadow.hide();
25383         }
25384         this.removeAllListeners();
25385         var pn = this.dom.parentNode;
25386         if(pn){
25387             pn.removeChild(this.dom);
25388         }
25389         Roo.Element.uncache(this.id);
25390     },
25391
25392     remove : function(){
25393         this.destroy();
25394     },
25395
25396     // private
25397     beginUpdate : function(){
25398         this.updating = true;
25399     },
25400
25401     // private
25402     endUpdate : function(){
25403         this.updating = false;
25404         this.sync(true);
25405     },
25406
25407     // private
25408     hideUnders : function(negOffset){
25409         if(this.shadow){
25410             this.shadow.hide();
25411         }
25412         this.hideShim();
25413     },
25414
25415     // private
25416     constrainXY : function(){
25417         if(this.constrain){
25418             var vw = Roo.lib.Dom.getViewWidth(),
25419                 vh = Roo.lib.Dom.getViewHeight();
25420             var s = Roo.get(document).getScroll();
25421
25422             var xy = this.getXY();
25423             var x = xy[0], y = xy[1];   
25424             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25425             // only move it if it needs it
25426             var moved = false;
25427             // first validate right/bottom
25428             if((x + w) > vw+s.left){
25429                 x = vw - w - this.shadowOffset;
25430                 moved = true;
25431             }
25432             if((y + h) > vh+s.top){
25433                 y = vh - h - this.shadowOffset;
25434                 moved = true;
25435             }
25436             // then make sure top/left isn't negative
25437             if(x < s.left){
25438                 x = s.left;
25439                 moved = true;
25440             }
25441             if(y < s.top){
25442                 y = s.top;
25443                 moved = true;
25444             }
25445             if(moved){
25446                 if(this.avoidY){
25447                     var ay = this.avoidY;
25448                     if(y <= ay && (y+h) >= ay){
25449                         y = ay-h-5;   
25450                     }
25451                 }
25452                 xy = [x, y];
25453                 this.storeXY(xy);
25454                 supr.setXY.call(this, xy);
25455                 this.sync();
25456             }
25457         }
25458     },
25459
25460     isVisible : function(){
25461         return this.visible;    
25462     },
25463
25464     // private
25465     showAction : function(){
25466         this.visible = true; // track visibility to prevent getStyle calls
25467         if(this.useDisplay === true){
25468             this.setDisplayed("");
25469         }else if(this.lastXY){
25470             supr.setXY.call(this, this.lastXY);
25471         }else if(this.lastLT){
25472             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25473         }
25474     },
25475
25476     // private
25477     hideAction : function(){
25478         this.visible = false;
25479         if(this.useDisplay === true){
25480             this.setDisplayed(false);
25481         }else{
25482             this.setLeftTop(-10000,-10000);
25483         }
25484     },
25485
25486     // overridden Element method
25487     setVisible : function(v, a, d, c, e){
25488         if(v){
25489             this.showAction();
25490         }
25491         if(a && v){
25492             var cb = function(){
25493                 this.sync(true);
25494                 if(c){
25495                     c();
25496                 }
25497             }.createDelegate(this);
25498             supr.setVisible.call(this, true, true, d, cb, e);
25499         }else{
25500             if(!v){
25501                 this.hideUnders(true);
25502             }
25503             var cb = c;
25504             if(a){
25505                 cb = function(){
25506                     this.hideAction();
25507                     if(c){
25508                         c();
25509                     }
25510                 }.createDelegate(this);
25511             }
25512             supr.setVisible.call(this, v, a, d, cb, e);
25513             if(v){
25514                 this.sync(true);
25515             }else if(!a){
25516                 this.hideAction();
25517             }
25518         }
25519     },
25520
25521     storeXY : function(xy){
25522         delete this.lastLT;
25523         this.lastXY = xy;
25524     },
25525
25526     storeLeftTop : function(left, top){
25527         delete this.lastXY;
25528         this.lastLT = [left, top];
25529     },
25530
25531     // private
25532     beforeFx : function(){
25533         this.beforeAction();
25534         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25535     },
25536
25537     // private
25538     afterFx : function(){
25539         Roo.Layer.superclass.afterFx.apply(this, arguments);
25540         this.sync(this.isVisible());
25541     },
25542
25543     // private
25544     beforeAction : function(){
25545         if(!this.updating && this.shadow){
25546             this.shadow.hide();
25547         }
25548     },
25549
25550     // overridden Element method
25551     setLeft : function(left){
25552         this.storeLeftTop(left, this.getTop(true));
25553         supr.setLeft.apply(this, arguments);
25554         this.sync();
25555     },
25556
25557     setTop : function(top){
25558         this.storeLeftTop(this.getLeft(true), top);
25559         supr.setTop.apply(this, arguments);
25560         this.sync();
25561     },
25562
25563     setLeftTop : function(left, top){
25564         this.storeLeftTop(left, top);
25565         supr.setLeftTop.apply(this, arguments);
25566         this.sync();
25567     },
25568
25569     setXY : function(xy, a, d, c, e){
25570         this.fixDisplay();
25571         this.beforeAction();
25572         this.storeXY(xy);
25573         var cb = this.createCB(c);
25574         supr.setXY.call(this, xy, a, d, cb, e);
25575         if(!a){
25576             cb();
25577         }
25578     },
25579
25580     // private
25581     createCB : function(c){
25582         var el = this;
25583         return function(){
25584             el.constrainXY();
25585             el.sync(true);
25586             if(c){
25587                 c();
25588             }
25589         };
25590     },
25591
25592     // overridden Element method
25593     setX : function(x, a, d, c, e){
25594         this.setXY([x, this.getY()], a, d, c, e);
25595     },
25596
25597     // overridden Element method
25598     setY : function(y, a, d, c, e){
25599         this.setXY([this.getX(), y], a, d, c, e);
25600     },
25601
25602     // overridden Element method
25603     setSize : function(w, h, a, d, c, e){
25604         this.beforeAction();
25605         var cb = this.createCB(c);
25606         supr.setSize.call(this, w, h, a, d, cb, e);
25607         if(!a){
25608             cb();
25609         }
25610     },
25611
25612     // overridden Element method
25613     setWidth : function(w, a, d, c, e){
25614         this.beforeAction();
25615         var cb = this.createCB(c);
25616         supr.setWidth.call(this, w, a, d, cb, e);
25617         if(!a){
25618             cb();
25619         }
25620     },
25621
25622     // overridden Element method
25623     setHeight : function(h, a, d, c, e){
25624         this.beforeAction();
25625         var cb = this.createCB(c);
25626         supr.setHeight.call(this, h, a, d, cb, e);
25627         if(!a){
25628             cb();
25629         }
25630     },
25631
25632     // overridden Element method
25633     setBounds : function(x, y, w, h, a, d, c, e){
25634         this.beforeAction();
25635         var cb = this.createCB(c);
25636         if(!a){
25637             this.storeXY([x, y]);
25638             supr.setXY.call(this, [x, y]);
25639             supr.setSize.call(this, w, h, a, d, cb, e);
25640             cb();
25641         }else{
25642             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25643         }
25644         return this;
25645     },
25646     
25647     /**
25648      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25649      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25650      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25651      * @param {Number} zindex The new z-index to set
25652      * @return {this} The Layer
25653      */
25654     setZIndex : function(zindex){
25655         this.zindex = zindex;
25656         this.setStyle("z-index", zindex + 2);
25657         if(this.shadow){
25658             this.shadow.setZIndex(zindex + 1);
25659         }
25660         if(this.shim){
25661             this.shim.setStyle("z-index", zindex);
25662         }
25663     }
25664 });
25665 })();/*
25666  * Based on:
25667  * Ext JS Library 1.1.1
25668  * Copyright(c) 2006-2007, Ext JS, LLC.
25669  *
25670  * Originally Released Under LGPL - original licence link has changed is not relivant.
25671  *
25672  * Fork - LGPL
25673  * <script type="text/javascript">
25674  */
25675
25676
25677 /**
25678  * @class Roo.Shadow
25679  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25680  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25681  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25682  * @constructor
25683  * Create a new Shadow
25684  * @param {Object} config The config object
25685  */
25686 Roo.Shadow = function(config){
25687     Roo.apply(this, config);
25688     if(typeof this.mode != "string"){
25689         this.mode = this.defaultMode;
25690     }
25691     var o = this.offset, a = {h: 0};
25692     var rad = Math.floor(this.offset/2);
25693     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25694         case "drop":
25695             a.w = 0;
25696             a.l = a.t = o;
25697             a.t -= 1;
25698             if(Roo.isIE){
25699                 a.l -= this.offset + rad;
25700                 a.t -= this.offset + rad;
25701                 a.w -= rad;
25702                 a.h -= rad;
25703                 a.t += 1;
25704             }
25705         break;
25706         case "sides":
25707             a.w = (o*2);
25708             a.l = -o;
25709             a.t = o-1;
25710             if(Roo.isIE){
25711                 a.l -= (this.offset - rad);
25712                 a.t -= this.offset + rad;
25713                 a.l += 1;
25714                 a.w -= (this.offset - rad)*2;
25715                 a.w -= rad + 1;
25716                 a.h -= 1;
25717             }
25718         break;
25719         case "frame":
25720             a.w = a.h = (o*2);
25721             a.l = a.t = -o;
25722             a.t += 1;
25723             a.h -= 2;
25724             if(Roo.isIE){
25725                 a.l -= (this.offset - rad);
25726                 a.t -= (this.offset - rad);
25727                 a.l += 1;
25728                 a.w -= (this.offset + rad + 1);
25729                 a.h -= (this.offset + rad);
25730                 a.h += 1;
25731             }
25732         break;
25733     };
25734
25735     this.adjusts = a;
25736 };
25737
25738 Roo.Shadow.prototype = {
25739     /**
25740      * @cfg {String} mode
25741      * The shadow display mode.  Supports the following options:<br />
25742      * sides: Shadow displays on both sides and bottom only<br />
25743      * frame: Shadow displays equally on all four sides<br />
25744      * drop: Traditional bottom-right drop shadow (default)
25745      */
25746     /**
25747      * @cfg {String} offset
25748      * The number of pixels to offset the shadow from the element (defaults to 4)
25749      */
25750     offset: 4,
25751
25752     // private
25753     defaultMode: "drop",
25754
25755     /**
25756      * Displays the shadow under the target element
25757      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25758      */
25759     show : function(target){
25760         target = Roo.get(target);
25761         if(!this.el){
25762             this.el = Roo.Shadow.Pool.pull();
25763             if(this.el.dom.nextSibling != target.dom){
25764                 this.el.insertBefore(target);
25765             }
25766         }
25767         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25768         if(Roo.isIE){
25769             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25770         }
25771         this.realign(
25772             target.getLeft(true),
25773             target.getTop(true),
25774             target.getWidth(),
25775             target.getHeight()
25776         );
25777         this.el.dom.style.display = "block";
25778     },
25779
25780     /**
25781      * Returns true if the shadow is visible, else false
25782      */
25783     isVisible : function(){
25784         return this.el ? true : false;  
25785     },
25786
25787     /**
25788      * Direct alignment when values are already available. Show must be called at least once before
25789      * calling this method to ensure it is initialized.
25790      * @param {Number} left The target element left position
25791      * @param {Number} top The target element top position
25792      * @param {Number} width The target element width
25793      * @param {Number} height The target element height
25794      */
25795     realign : function(l, t, w, h){
25796         if(!this.el){
25797             return;
25798         }
25799         var a = this.adjusts, d = this.el.dom, s = d.style;
25800         var iea = 0;
25801         s.left = (l+a.l)+"px";
25802         s.top = (t+a.t)+"px";
25803         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25804  
25805         if(s.width != sws || s.height != shs){
25806             s.width = sws;
25807             s.height = shs;
25808             if(!Roo.isIE){
25809                 var cn = d.childNodes;
25810                 var sww = Math.max(0, (sw-12))+"px";
25811                 cn[0].childNodes[1].style.width = sww;
25812                 cn[1].childNodes[1].style.width = sww;
25813                 cn[2].childNodes[1].style.width = sww;
25814                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25815             }
25816         }
25817     },
25818
25819     /**
25820      * Hides this shadow
25821      */
25822     hide : function(){
25823         if(this.el){
25824             this.el.dom.style.display = "none";
25825             Roo.Shadow.Pool.push(this.el);
25826             delete this.el;
25827         }
25828     },
25829
25830     /**
25831      * Adjust the z-index of this shadow
25832      * @param {Number} zindex The new z-index
25833      */
25834     setZIndex : function(z){
25835         this.zIndex = z;
25836         if(this.el){
25837             this.el.setStyle("z-index", z);
25838         }
25839     }
25840 };
25841
25842 // Private utility class that manages the internal Shadow cache
25843 Roo.Shadow.Pool = function(){
25844     var p = [];
25845     var markup = Roo.isIE ?
25846                  '<div class="x-ie-shadow"></div>' :
25847                  '<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>';
25848     return {
25849         pull : function(){
25850             var sh = p.shift();
25851             if(!sh){
25852                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25853                 sh.autoBoxAdjust = false;
25854             }
25855             return sh;
25856         },
25857
25858         push : function(sh){
25859             p.push(sh);
25860         }
25861     };
25862 }();/*
25863  * Based on:
25864  * Ext JS Library 1.1.1
25865  * Copyright(c) 2006-2007, Ext JS, LLC.
25866  *
25867  * Originally Released Under LGPL - original licence link has changed is not relivant.
25868  *
25869  * Fork - LGPL
25870  * <script type="text/javascript">
25871  */
25872
25873
25874 /**
25875  * @class Roo.SplitBar
25876  * @extends Roo.util.Observable
25877  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25878  * <br><br>
25879  * Usage:
25880  * <pre><code>
25881 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25882                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25883 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25884 split.minSize = 100;
25885 split.maxSize = 600;
25886 split.animate = true;
25887 split.on('moved', splitterMoved);
25888 </code></pre>
25889  * @constructor
25890  * Create a new SplitBar
25891  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25892  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25893  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25894  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25895                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25896                         position of the SplitBar).
25897  */
25898 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25899     
25900     /** @private */
25901     this.el = Roo.get(dragElement, true);
25902     this.el.dom.unselectable = "on";
25903     /** @private */
25904     this.resizingEl = Roo.get(resizingElement, true);
25905
25906     /**
25907      * @private
25908      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25909      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25910      * @type Number
25911      */
25912     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25913     
25914     /**
25915      * The minimum size of the resizing element. (Defaults to 0)
25916      * @type Number
25917      */
25918     this.minSize = 0;
25919     
25920     /**
25921      * The maximum size of the resizing element. (Defaults to 2000)
25922      * @type Number
25923      */
25924     this.maxSize = 2000;
25925     
25926     /**
25927      * Whether to animate the transition to the new size
25928      * @type Boolean
25929      */
25930     this.animate = false;
25931     
25932     /**
25933      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25934      * @type Boolean
25935      */
25936     this.useShim = false;
25937     
25938     /** @private */
25939     this.shim = null;
25940     
25941     if(!existingProxy){
25942         /** @private */
25943         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25944     }else{
25945         this.proxy = Roo.get(existingProxy).dom;
25946     }
25947     /** @private */
25948     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
25949     
25950     /** @private */
25951     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
25952     
25953     /** @private */
25954     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
25955     
25956     /** @private */
25957     this.dragSpecs = {};
25958     
25959     /**
25960      * @private The adapter to use to positon and resize elements
25961      */
25962     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
25963     this.adapter.init(this);
25964     
25965     if(this.orientation == Roo.SplitBar.HORIZONTAL){
25966         /** @private */
25967         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
25968         this.el.addClass("x-splitbar-h");
25969     }else{
25970         /** @private */
25971         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
25972         this.el.addClass("x-splitbar-v");
25973     }
25974     
25975     this.addEvents({
25976         /**
25977          * @event resize
25978          * Fires when the splitter is moved (alias for {@link #event-moved})
25979          * @param {Roo.SplitBar} this
25980          * @param {Number} newSize the new width or height
25981          */
25982         "resize" : true,
25983         /**
25984          * @event moved
25985          * Fires when the splitter is moved
25986          * @param {Roo.SplitBar} this
25987          * @param {Number} newSize the new width or height
25988          */
25989         "moved" : true,
25990         /**
25991          * @event beforeresize
25992          * Fires before the splitter is dragged
25993          * @param {Roo.SplitBar} this
25994          */
25995         "beforeresize" : true,
25996
25997         "beforeapply" : true
25998     });
25999
26000     Roo.util.Observable.call(this);
26001 };
26002
26003 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26004     onStartProxyDrag : function(x, y){
26005         this.fireEvent("beforeresize", this);
26006         if(!this.overlay){
26007             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26008             o.unselectable();
26009             o.enableDisplayMode("block");
26010             // all splitbars share the same overlay
26011             Roo.SplitBar.prototype.overlay = o;
26012         }
26013         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26014         this.overlay.show();
26015         Roo.get(this.proxy).setDisplayed("block");
26016         var size = this.adapter.getElementSize(this);
26017         this.activeMinSize = this.getMinimumSize();;
26018         this.activeMaxSize = this.getMaximumSize();;
26019         var c1 = size - this.activeMinSize;
26020         var c2 = Math.max(this.activeMaxSize - size, 0);
26021         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26022             this.dd.resetConstraints();
26023             this.dd.setXConstraint(
26024                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26025                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26026             );
26027             this.dd.setYConstraint(0, 0);
26028         }else{
26029             this.dd.resetConstraints();
26030             this.dd.setXConstraint(0, 0);
26031             this.dd.setYConstraint(
26032                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26033                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26034             );
26035          }
26036         this.dragSpecs.startSize = size;
26037         this.dragSpecs.startPoint = [x, y];
26038         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26039     },
26040     
26041     /** 
26042      * @private Called after the drag operation by the DDProxy
26043      */
26044     onEndProxyDrag : function(e){
26045         Roo.get(this.proxy).setDisplayed(false);
26046         var endPoint = Roo.lib.Event.getXY(e);
26047         if(this.overlay){
26048             this.overlay.hide();
26049         }
26050         var newSize;
26051         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26052             newSize = this.dragSpecs.startSize + 
26053                 (this.placement == Roo.SplitBar.LEFT ?
26054                     endPoint[0] - this.dragSpecs.startPoint[0] :
26055                     this.dragSpecs.startPoint[0] - endPoint[0]
26056                 );
26057         }else{
26058             newSize = this.dragSpecs.startSize + 
26059                 (this.placement == Roo.SplitBar.TOP ?
26060                     endPoint[1] - this.dragSpecs.startPoint[1] :
26061                     this.dragSpecs.startPoint[1] - endPoint[1]
26062                 );
26063         }
26064         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26065         if(newSize != this.dragSpecs.startSize){
26066             if(this.fireEvent('beforeapply', this, newSize) !== false){
26067                 this.adapter.setElementSize(this, newSize);
26068                 this.fireEvent("moved", this, newSize);
26069                 this.fireEvent("resize", this, newSize);
26070             }
26071         }
26072     },
26073     
26074     /**
26075      * Get the adapter this SplitBar uses
26076      * @return The adapter object
26077      */
26078     getAdapter : function(){
26079         return this.adapter;
26080     },
26081     
26082     /**
26083      * Set the adapter this SplitBar uses
26084      * @param {Object} adapter A SplitBar adapter object
26085      */
26086     setAdapter : function(adapter){
26087         this.adapter = adapter;
26088         this.adapter.init(this);
26089     },
26090     
26091     /**
26092      * Gets the minimum size for the resizing element
26093      * @return {Number} The minimum size
26094      */
26095     getMinimumSize : function(){
26096         return this.minSize;
26097     },
26098     
26099     /**
26100      * Sets the minimum size for the resizing element
26101      * @param {Number} minSize The minimum size
26102      */
26103     setMinimumSize : function(minSize){
26104         this.minSize = minSize;
26105     },
26106     
26107     /**
26108      * Gets the maximum size for the resizing element
26109      * @return {Number} The maximum size
26110      */
26111     getMaximumSize : function(){
26112         return this.maxSize;
26113     },
26114     
26115     /**
26116      * Sets the maximum size for the resizing element
26117      * @param {Number} maxSize The maximum size
26118      */
26119     setMaximumSize : function(maxSize){
26120         this.maxSize = maxSize;
26121     },
26122     
26123     /**
26124      * Sets the initialize size for the resizing element
26125      * @param {Number} size The initial size
26126      */
26127     setCurrentSize : function(size){
26128         var oldAnimate = this.animate;
26129         this.animate = false;
26130         this.adapter.setElementSize(this, size);
26131         this.animate = oldAnimate;
26132     },
26133     
26134     /**
26135      * Destroy this splitbar. 
26136      * @param {Boolean} removeEl True to remove the element
26137      */
26138     destroy : function(removeEl){
26139         if(this.shim){
26140             this.shim.remove();
26141         }
26142         this.dd.unreg();
26143         this.proxy.parentNode.removeChild(this.proxy);
26144         if(removeEl){
26145             this.el.remove();
26146         }
26147     }
26148 });
26149
26150 /**
26151  * @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.
26152  */
26153 Roo.SplitBar.createProxy = function(dir){
26154     var proxy = new Roo.Element(document.createElement("div"));
26155     proxy.unselectable();
26156     var cls = 'x-splitbar-proxy';
26157     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26158     document.body.appendChild(proxy.dom);
26159     return proxy.dom;
26160 };
26161
26162 /** 
26163  * @class Roo.SplitBar.BasicLayoutAdapter
26164  * Default Adapter. It assumes the splitter and resizing element are not positioned
26165  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26166  */
26167 Roo.SplitBar.BasicLayoutAdapter = function(){
26168 };
26169
26170 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26171     // do nothing for now
26172     init : function(s){
26173     
26174     },
26175     /**
26176      * Called before drag operations to get the current size of the resizing element. 
26177      * @param {Roo.SplitBar} s The SplitBar using this adapter
26178      */
26179      getElementSize : function(s){
26180         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26181             return s.resizingEl.getWidth();
26182         }else{
26183             return s.resizingEl.getHeight();
26184         }
26185     },
26186     
26187     /**
26188      * Called after drag operations to set the size of the resizing element.
26189      * @param {Roo.SplitBar} s The SplitBar using this adapter
26190      * @param {Number} newSize The new size to set
26191      * @param {Function} onComplete A function to be invoked when resizing is complete
26192      */
26193     setElementSize : function(s, newSize, onComplete){
26194         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26195             if(!s.animate){
26196                 s.resizingEl.setWidth(newSize);
26197                 if(onComplete){
26198                     onComplete(s, newSize);
26199                 }
26200             }else{
26201                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26202             }
26203         }else{
26204             
26205             if(!s.animate){
26206                 s.resizingEl.setHeight(newSize);
26207                 if(onComplete){
26208                     onComplete(s, newSize);
26209                 }
26210             }else{
26211                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26212             }
26213         }
26214     }
26215 };
26216
26217 /** 
26218  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26219  * @extends Roo.SplitBar.BasicLayoutAdapter
26220  * Adapter that  moves the splitter element to align with the resized sizing element. 
26221  * Used with an absolute positioned SplitBar.
26222  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26223  * document.body, make sure you assign an id to the body element.
26224  */
26225 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26226     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26227     this.container = Roo.get(container);
26228 };
26229
26230 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26231     init : function(s){
26232         this.basic.init(s);
26233     },
26234     
26235     getElementSize : function(s){
26236         return this.basic.getElementSize(s);
26237     },
26238     
26239     setElementSize : function(s, newSize, onComplete){
26240         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26241     },
26242     
26243     moveSplitter : function(s){
26244         var yes = Roo.SplitBar;
26245         switch(s.placement){
26246             case yes.LEFT:
26247                 s.el.setX(s.resizingEl.getRight());
26248                 break;
26249             case yes.RIGHT:
26250                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26251                 break;
26252             case yes.TOP:
26253                 s.el.setY(s.resizingEl.getBottom());
26254                 break;
26255             case yes.BOTTOM:
26256                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26257                 break;
26258         }
26259     }
26260 };
26261
26262 /**
26263  * Orientation constant - Create a vertical SplitBar
26264  * @static
26265  * @type Number
26266  */
26267 Roo.SplitBar.VERTICAL = 1;
26268
26269 /**
26270  * Orientation constant - Create a horizontal SplitBar
26271  * @static
26272  * @type Number
26273  */
26274 Roo.SplitBar.HORIZONTAL = 2;
26275
26276 /**
26277  * Placement constant - The resizing element is to the left of the splitter element
26278  * @static
26279  * @type Number
26280  */
26281 Roo.SplitBar.LEFT = 1;
26282
26283 /**
26284  * Placement constant - The resizing element is to the right of the splitter element
26285  * @static
26286  * @type Number
26287  */
26288 Roo.SplitBar.RIGHT = 2;
26289
26290 /**
26291  * Placement constant - The resizing element is positioned above the splitter element
26292  * @static
26293  * @type Number
26294  */
26295 Roo.SplitBar.TOP = 3;
26296
26297 /**
26298  * Placement constant - The resizing element is positioned under splitter element
26299  * @static
26300  * @type Number
26301  */
26302 Roo.SplitBar.BOTTOM = 4;
26303 /*
26304  * Based on:
26305  * Ext JS Library 1.1.1
26306  * Copyright(c) 2006-2007, Ext JS, LLC.
26307  *
26308  * Originally Released Under LGPL - original licence link has changed is not relivant.
26309  *
26310  * Fork - LGPL
26311  * <script type="text/javascript">
26312  */
26313
26314 /**
26315  * @class Roo.View
26316  * @extends Roo.util.Observable
26317  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26318  * This class also supports single and multi selection modes. <br>
26319  * Create a data model bound view:
26320  <pre><code>
26321  var store = new Roo.data.Store(...);
26322
26323  var view = new Roo.View({
26324     el : "my-element",
26325     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26326  
26327     singleSelect: true,
26328     selectedClass: "ydataview-selected",
26329     store: store
26330  });
26331
26332  // listen for node click?
26333  view.on("click", function(vw, index, node, e){
26334  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26335  });
26336
26337  // load XML data
26338  dataModel.load("foobar.xml");
26339  </code></pre>
26340  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26341  * <br><br>
26342  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26343  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26344  * 
26345  * Note: old style constructor is still suported (container, template, config)
26346  * 
26347  * @constructor
26348  * Create a new View
26349  * @param {Object} config The config object
26350  * 
26351  */
26352 Roo.View = function(config, depreciated_tpl, depreciated_config){
26353     
26354     this.parent = false;
26355     
26356     if (typeof(depreciated_tpl) == 'undefined') {
26357         // new way.. - universal constructor.
26358         Roo.apply(this, config);
26359         this.el  = Roo.get(this.el);
26360     } else {
26361         // old format..
26362         this.el  = Roo.get(config);
26363         this.tpl = depreciated_tpl;
26364         Roo.apply(this, depreciated_config);
26365     }
26366     this.wrapEl  = this.el.wrap().wrap();
26367     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26368     
26369     
26370     if(typeof(this.tpl) == "string"){
26371         this.tpl = new Roo.Template(this.tpl);
26372     } else {
26373         // support xtype ctors..
26374         this.tpl = new Roo.factory(this.tpl, Roo);
26375     }
26376     
26377     
26378     this.tpl.compile();
26379     
26380     /** @private */
26381     this.addEvents({
26382         /**
26383          * @event beforeclick
26384          * Fires before a click is processed. Returns false to cancel the default action.
26385          * @param {Roo.View} this
26386          * @param {Number} index The index of the target node
26387          * @param {HTMLElement} node The target node
26388          * @param {Roo.EventObject} e The raw event object
26389          */
26390             "beforeclick" : true,
26391         /**
26392          * @event click
26393          * Fires when a template node is clicked.
26394          * @param {Roo.View} this
26395          * @param {Number} index The index of the target node
26396          * @param {HTMLElement} node The target node
26397          * @param {Roo.EventObject} e The raw event object
26398          */
26399             "click" : true,
26400         /**
26401          * @event dblclick
26402          * Fires when a template node is double clicked.
26403          * @param {Roo.View} this
26404          * @param {Number} index The index of the target node
26405          * @param {HTMLElement} node The target node
26406          * @param {Roo.EventObject} e The raw event object
26407          */
26408             "dblclick" : true,
26409         /**
26410          * @event contextmenu
26411          * Fires when a template node is right clicked.
26412          * @param {Roo.View} this
26413          * @param {Number} index The index of the target node
26414          * @param {HTMLElement} node The target node
26415          * @param {Roo.EventObject} e The raw event object
26416          */
26417             "contextmenu" : true,
26418         /**
26419          * @event selectionchange
26420          * Fires when the selected nodes change.
26421          * @param {Roo.View} this
26422          * @param {Array} selections Array of the selected nodes
26423          */
26424             "selectionchange" : true,
26425     
26426         /**
26427          * @event beforeselect
26428          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26429          * @param {Roo.View} this
26430          * @param {HTMLElement} node The node to be selected
26431          * @param {Array} selections Array of currently selected nodes
26432          */
26433             "beforeselect" : true,
26434         /**
26435          * @event preparedata
26436          * Fires on every row to render, to allow you to change the data.
26437          * @param {Roo.View} this
26438          * @param {Object} data to be rendered (change this)
26439          */
26440           "preparedata" : true
26441           
26442           
26443         });
26444
26445
26446
26447     this.el.on({
26448         "click": this.onClick,
26449         "dblclick": this.onDblClick,
26450         "contextmenu": this.onContextMenu,
26451         scope:this
26452     });
26453
26454     this.selections = [];
26455     this.nodes = [];
26456     this.cmp = new Roo.CompositeElementLite([]);
26457     if(this.store){
26458         this.store = Roo.factory(this.store, Roo.data);
26459         this.setStore(this.store, true);
26460     }
26461     
26462     if ( this.footer && this.footer.xtype) {
26463            
26464          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26465         
26466         this.footer.dataSource = this.store;
26467         this.footer.container = fctr;
26468         this.footer = Roo.factory(this.footer, Roo);
26469         fctr.insertFirst(this.el);
26470         
26471         // this is a bit insane - as the paging toolbar seems to detach the el..
26472 //        dom.parentNode.parentNode.parentNode
26473          // they get detached?
26474     }
26475     
26476     
26477     Roo.View.superclass.constructor.call(this);
26478     
26479     
26480 };
26481
26482 Roo.extend(Roo.View, Roo.util.Observable, {
26483     
26484      /**
26485      * @cfg {Roo.data.Store} store Data store to load data from.
26486      */
26487     store : false,
26488     
26489     /**
26490      * @cfg {String|Roo.Element} el The container element.
26491      */
26492     el : '',
26493     
26494     /**
26495      * @cfg {String|Roo.Template} tpl The template used by this View 
26496      */
26497     tpl : false,
26498     /**
26499      * @cfg {String} dataName the named area of the template to use as the data area
26500      *                          Works with domtemplates roo-name="name"
26501      */
26502     dataName: false,
26503     /**
26504      * @cfg {String} selectedClass The css class to add to selected nodes
26505      */
26506     selectedClass : "x-view-selected",
26507      /**
26508      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26509      */
26510     emptyText : "",
26511     
26512     /**
26513      * @cfg {String} text to display on mask (default Loading)
26514      */
26515     mask : false,
26516     /**
26517      * @cfg {Boolean} multiSelect Allow multiple selection
26518      */
26519     multiSelect : false,
26520     /**
26521      * @cfg {Boolean} singleSelect Allow single selection
26522      */
26523     singleSelect:  false,
26524     
26525     /**
26526      * @cfg {Boolean} toggleSelect - selecting 
26527      */
26528     toggleSelect : false,
26529     
26530     /**
26531      * @cfg {Boolean} tickable - selecting 
26532      */
26533     tickable : false,
26534     
26535     /**
26536      * Returns the element this view is bound to.
26537      * @return {Roo.Element}
26538      */
26539     getEl : function(){
26540         return this.wrapEl;
26541     },
26542     
26543     
26544
26545     /**
26546      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26547      */
26548     refresh : function(){
26549         //Roo.log('refresh');
26550         var t = this.tpl;
26551         
26552         // if we are using something like 'domtemplate', then
26553         // the what gets used is:
26554         // t.applySubtemplate(NAME, data, wrapping data..)
26555         // the outer template then get' applied with
26556         //     the store 'extra data'
26557         // and the body get's added to the
26558         //      roo-name="data" node?
26559         //      <span class='roo-tpl-{name}'></span> ?????
26560         
26561         
26562         
26563         this.clearSelections();
26564         this.el.update("");
26565         var html = [];
26566         var records = this.store.getRange();
26567         if(records.length < 1) {
26568             
26569             // is this valid??  = should it render a template??
26570             
26571             this.el.update(this.emptyText);
26572             return;
26573         }
26574         var el = this.el;
26575         if (this.dataName) {
26576             this.el.update(t.apply(this.store.meta)); //????
26577             el = this.el.child('.roo-tpl-' + this.dataName);
26578         }
26579         
26580         for(var i = 0, len = records.length; i < len; i++){
26581             var data = this.prepareData(records[i].data, i, records[i]);
26582             this.fireEvent("preparedata", this, data, i, records[i]);
26583             
26584             var d = Roo.apply({}, data);
26585             
26586             if(this.tickable){
26587                 Roo.apply(d, {'roo-id' : Roo.id()});
26588                 
26589                 var _this = this;
26590             
26591                 Roo.each(this.parent.item, function(item){
26592                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26593                         return;
26594                     }
26595                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26596                 });
26597             }
26598             
26599             html[html.length] = Roo.util.Format.trim(
26600                 this.dataName ?
26601                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26602                     t.apply(d)
26603             );
26604         }
26605         
26606         
26607         
26608         el.update(html.join(""));
26609         this.nodes = el.dom.childNodes;
26610         this.updateIndexes(0);
26611     },
26612     
26613
26614     /**
26615      * Function to override to reformat the data that is sent to
26616      * the template for each node.
26617      * DEPRICATED - use the preparedata event handler.
26618      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26619      * a JSON object for an UpdateManager bound view).
26620      */
26621     prepareData : function(data, index, record)
26622     {
26623         this.fireEvent("preparedata", this, data, index, record);
26624         return data;
26625     },
26626
26627     onUpdate : function(ds, record){
26628         // Roo.log('on update');   
26629         this.clearSelections();
26630         var index = this.store.indexOf(record);
26631         var n = this.nodes[index];
26632         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26633         n.parentNode.removeChild(n);
26634         this.updateIndexes(index, index);
26635     },
26636
26637     
26638     
26639 // --------- FIXME     
26640     onAdd : function(ds, records, index)
26641     {
26642         //Roo.log(['on Add', ds, records, index] );        
26643         this.clearSelections();
26644         if(this.nodes.length == 0){
26645             this.refresh();
26646             return;
26647         }
26648         var n = this.nodes[index];
26649         for(var i = 0, len = records.length; i < len; i++){
26650             var d = this.prepareData(records[i].data, i, records[i]);
26651             if(n){
26652                 this.tpl.insertBefore(n, d);
26653             }else{
26654                 
26655                 this.tpl.append(this.el, d);
26656             }
26657         }
26658         this.updateIndexes(index);
26659     },
26660
26661     onRemove : function(ds, record, index){
26662        // Roo.log('onRemove');
26663         this.clearSelections();
26664         var el = this.dataName  ?
26665             this.el.child('.roo-tpl-' + this.dataName) :
26666             this.el; 
26667         
26668         el.dom.removeChild(this.nodes[index]);
26669         this.updateIndexes(index);
26670     },
26671
26672     /**
26673      * Refresh an individual node.
26674      * @param {Number} index
26675      */
26676     refreshNode : function(index){
26677         this.onUpdate(this.store, this.store.getAt(index));
26678     },
26679
26680     updateIndexes : function(startIndex, endIndex){
26681         var ns = this.nodes;
26682         startIndex = startIndex || 0;
26683         endIndex = endIndex || ns.length - 1;
26684         for(var i = startIndex; i <= endIndex; i++){
26685             ns[i].nodeIndex = i;
26686         }
26687     },
26688
26689     /**
26690      * Changes the data store this view uses and refresh the view.
26691      * @param {Store} store
26692      */
26693     setStore : function(store, initial){
26694         if(!initial && this.store){
26695             this.store.un("datachanged", this.refresh);
26696             this.store.un("add", this.onAdd);
26697             this.store.un("remove", this.onRemove);
26698             this.store.un("update", this.onUpdate);
26699             this.store.un("clear", this.refresh);
26700             this.store.un("beforeload", this.onBeforeLoad);
26701             this.store.un("load", this.onLoad);
26702             this.store.un("loadexception", this.onLoad);
26703         }
26704         if(store){
26705           
26706             store.on("datachanged", this.refresh, this);
26707             store.on("add", this.onAdd, this);
26708             store.on("remove", this.onRemove, this);
26709             store.on("update", this.onUpdate, this);
26710             store.on("clear", this.refresh, this);
26711             store.on("beforeload", this.onBeforeLoad, this);
26712             store.on("load", this.onLoad, this);
26713             store.on("loadexception", this.onLoad, this);
26714         }
26715         
26716         if(store){
26717             this.refresh();
26718         }
26719     },
26720     /**
26721      * onbeforeLoad - masks the loading area.
26722      *
26723      */
26724     onBeforeLoad : function(store,opts)
26725     {
26726          //Roo.log('onBeforeLoad');   
26727         if (!opts.add) {
26728             this.el.update("");
26729         }
26730         this.el.mask(this.mask ? this.mask : "Loading" ); 
26731     },
26732     onLoad : function ()
26733     {
26734         this.el.unmask();
26735     },
26736     
26737
26738     /**
26739      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26740      * @param {HTMLElement} node
26741      * @return {HTMLElement} The template node
26742      */
26743     findItemFromChild : function(node){
26744         var el = this.dataName  ?
26745             this.el.child('.roo-tpl-' + this.dataName,true) :
26746             this.el.dom; 
26747         
26748         if(!node || node.parentNode == el){
26749                     return node;
26750             }
26751             var p = node.parentNode;
26752             while(p && p != el){
26753             if(p.parentNode == el){
26754                 return p;
26755             }
26756             p = p.parentNode;
26757         }
26758             return null;
26759     },
26760
26761     /** @ignore */
26762     onClick : function(e){
26763         var item = this.findItemFromChild(e.getTarget());
26764         if(item){
26765             var index = this.indexOf(item);
26766             if(this.onItemClick(item, index, e) !== false){
26767                 this.fireEvent("click", this, index, item, e);
26768             }
26769         }else{
26770             this.clearSelections();
26771         }
26772     },
26773
26774     /** @ignore */
26775     onContextMenu : function(e){
26776         var item = this.findItemFromChild(e.getTarget());
26777         if(item){
26778             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26779         }
26780     },
26781
26782     /** @ignore */
26783     onDblClick : function(e){
26784         var item = this.findItemFromChild(e.getTarget());
26785         if(item){
26786             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26787         }
26788     },
26789
26790     onItemClick : function(item, index, e)
26791     {
26792         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26793             return false;
26794         }
26795         if (this.toggleSelect) {
26796             var m = this.isSelected(item) ? 'unselect' : 'select';
26797             //Roo.log(m);
26798             var _t = this;
26799             _t[m](item, true, false);
26800             return true;
26801         }
26802         if(this.multiSelect || this.singleSelect){
26803             if(this.multiSelect && e.shiftKey && this.lastSelection){
26804                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26805             }else{
26806                 this.select(item, this.multiSelect && e.ctrlKey);
26807                 this.lastSelection = item;
26808             }
26809             
26810             if(!this.tickable){
26811                 e.preventDefault();
26812             }
26813             
26814         }
26815         return true;
26816     },
26817
26818     /**
26819      * Get the number of selected nodes.
26820      * @return {Number}
26821      */
26822     getSelectionCount : function(){
26823         return this.selections.length;
26824     },
26825
26826     /**
26827      * Get the currently selected nodes.
26828      * @return {Array} An array of HTMLElements
26829      */
26830     getSelectedNodes : function(){
26831         return this.selections;
26832     },
26833
26834     /**
26835      * Get the indexes of the selected nodes.
26836      * @return {Array}
26837      */
26838     getSelectedIndexes : function(){
26839         var indexes = [], s = this.selections;
26840         for(var i = 0, len = s.length; i < len; i++){
26841             indexes.push(s[i].nodeIndex);
26842         }
26843         return indexes;
26844     },
26845
26846     /**
26847      * Clear all selections
26848      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26849      */
26850     clearSelections : function(suppressEvent){
26851         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26852             this.cmp.elements = this.selections;
26853             this.cmp.removeClass(this.selectedClass);
26854             this.selections = [];
26855             if(!suppressEvent){
26856                 this.fireEvent("selectionchange", this, this.selections);
26857             }
26858         }
26859     },
26860
26861     /**
26862      * Returns true if the passed node is selected
26863      * @param {HTMLElement/Number} node The node or node index
26864      * @return {Boolean}
26865      */
26866     isSelected : function(node){
26867         var s = this.selections;
26868         if(s.length < 1){
26869             return false;
26870         }
26871         node = this.getNode(node);
26872         return s.indexOf(node) !== -1;
26873     },
26874
26875     /**
26876      * Selects nodes.
26877      * @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
26878      * @param {Boolean} keepExisting (optional) true to keep existing selections
26879      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26880      */
26881     select : function(nodeInfo, keepExisting, suppressEvent){
26882         if(nodeInfo instanceof Array){
26883             if(!keepExisting){
26884                 this.clearSelections(true);
26885             }
26886             for(var i = 0, len = nodeInfo.length; i < len; i++){
26887                 this.select(nodeInfo[i], true, true);
26888             }
26889             return;
26890         } 
26891         var node = this.getNode(nodeInfo);
26892         if(!node || this.isSelected(node)){
26893             return; // already selected.
26894         }
26895         if(!keepExisting){
26896             this.clearSelections(true);
26897         }
26898         
26899         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26900             Roo.fly(node).addClass(this.selectedClass);
26901             this.selections.push(node);
26902             if(!suppressEvent){
26903                 this.fireEvent("selectionchange", this, this.selections);
26904             }
26905         }
26906         
26907         
26908     },
26909       /**
26910      * Unselects nodes.
26911      * @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
26912      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26913      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26914      */
26915     unselect : function(nodeInfo, keepExisting, suppressEvent)
26916     {
26917         if(nodeInfo instanceof Array){
26918             Roo.each(this.selections, function(s) {
26919                 this.unselect(s, nodeInfo);
26920             }, this);
26921             return;
26922         }
26923         var node = this.getNode(nodeInfo);
26924         if(!node || !this.isSelected(node)){
26925             //Roo.log("not selected");
26926             return; // not selected.
26927         }
26928         // fireevent???
26929         var ns = [];
26930         Roo.each(this.selections, function(s) {
26931             if (s == node ) {
26932                 Roo.fly(node).removeClass(this.selectedClass);
26933
26934                 return;
26935             }
26936             ns.push(s);
26937         },this);
26938         
26939         this.selections= ns;
26940         this.fireEvent("selectionchange", this, this.selections);
26941     },
26942
26943     /**
26944      * Gets a template node.
26945      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26946      * @return {HTMLElement} The node or null if it wasn't found
26947      */
26948     getNode : function(nodeInfo){
26949         if(typeof nodeInfo == "string"){
26950             return document.getElementById(nodeInfo);
26951         }else if(typeof nodeInfo == "number"){
26952             return this.nodes[nodeInfo];
26953         }
26954         return nodeInfo;
26955     },
26956
26957     /**
26958      * Gets a range template nodes.
26959      * @param {Number} startIndex
26960      * @param {Number} endIndex
26961      * @return {Array} An array of nodes
26962      */
26963     getNodes : function(start, end){
26964         var ns = this.nodes;
26965         start = start || 0;
26966         end = typeof end == "undefined" ? ns.length - 1 : end;
26967         var nodes = [];
26968         if(start <= end){
26969             for(var i = start; i <= end; i++){
26970                 nodes.push(ns[i]);
26971             }
26972         } else{
26973             for(var i = start; i >= end; i--){
26974                 nodes.push(ns[i]);
26975             }
26976         }
26977         return nodes;
26978     },
26979
26980     /**
26981      * Finds the index of the passed node
26982      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26983      * @return {Number} The index of the node or -1
26984      */
26985     indexOf : function(node){
26986         node = this.getNode(node);
26987         if(typeof node.nodeIndex == "number"){
26988             return node.nodeIndex;
26989         }
26990         var ns = this.nodes;
26991         for(var i = 0, len = ns.length; i < len; i++){
26992             if(ns[i] == node){
26993                 return i;
26994             }
26995         }
26996         return -1;
26997     }
26998 });
26999 /*
27000  * Based on:
27001  * Ext JS Library 1.1.1
27002  * Copyright(c) 2006-2007, Ext JS, LLC.
27003  *
27004  * Originally Released Under LGPL - original licence link has changed is not relivant.
27005  *
27006  * Fork - LGPL
27007  * <script type="text/javascript">
27008  */
27009
27010 /**
27011  * @class Roo.JsonView
27012  * @extends Roo.View
27013  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27014 <pre><code>
27015 var view = new Roo.JsonView({
27016     container: "my-element",
27017     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27018     multiSelect: true, 
27019     jsonRoot: "data" 
27020 });
27021
27022 // listen for node click?
27023 view.on("click", function(vw, index, node, e){
27024     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27025 });
27026
27027 // direct load of JSON data
27028 view.load("foobar.php");
27029
27030 // Example from my blog list
27031 var tpl = new Roo.Template(
27032     '&lt;div class="entry"&gt;' +
27033     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27034     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27035     "&lt;/div&gt;&lt;hr /&gt;"
27036 );
27037
27038 var moreView = new Roo.JsonView({
27039     container :  "entry-list", 
27040     template : tpl,
27041     jsonRoot: "posts"
27042 });
27043 moreView.on("beforerender", this.sortEntries, this);
27044 moreView.load({
27045     url: "/blog/get-posts.php",
27046     params: "allposts=true",
27047     text: "Loading Blog Entries..."
27048 });
27049 </code></pre>
27050
27051 * Note: old code is supported with arguments : (container, template, config)
27052
27053
27054  * @constructor
27055  * Create a new JsonView
27056  * 
27057  * @param {Object} config The config object
27058  * 
27059  */
27060 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27061     
27062     
27063     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27064
27065     var um = this.el.getUpdateManager();
27066     um.setRenderer(this);
27067     um.on("update", this.onLoad, this);
27068     um.on("failure", this.onLoadException, this);
27069
27070     /**
27071      * @event beforerender
27072      * Fires before rendering of the downloaded JSON data.
27073      * @param {Roo.JsonView} this
27074      * @param {Object} data The JSON data loaded
27075      */
27076     /**
27077      * @event load
27078      * Fires when data is loaded.
27079      * @param {Roo.JsonView} this
27080      * @param {Object} data The JSON data loaded
27081      * @param {Object} response The raw Connect response object
27082      */
27083     /**
27084      * @event loadexception
27085      * Fires when loading fails.
27086      * @param {Roo.JsonView} this
27087      * @param {Object} response The raw Connect response object
27088      */
27089     this.addEvents({
27090         'beforerender' : true,
27091         'load' : true,
27092         'loadexception' : true
27093     });
27094 };
27095 Roo.extend(Roo.JsonView, Roo.View, {
27096     /**
27097      * @type {String} The root property in the loaded JSON object that contains the data
27098      */
27099     jsonRoot : "",
27100
27101     /**
27102      * Refreshes the view.
27103      */
27104     refresh : function(){
27105         this.clearSelections();
27106         this.el.update("");
27107         var html = [];
27108         var o = this.jsonData;
27109         if(o && o.length > 0){
27110             for(var i = 0, len = o.length; i < len; i++){
27111                 var data = this.prepareData(o[i], i, o);
27112                 html[html.length] = this.tpl.apply(data);
27113             }
27114         }else{
27115             html.push(this.emptyText);
27116         }
27117         this.el.update(html.join(""));
27118         this.nodes = this.el.dom.childNodes;
27119         this.updateIndexes(0);
27120     },
27121
27122     /**
27123      * 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.
27124      * @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:
27125      <pre><code>
27126      view.load({
27127          url: "your-url.php",
27128          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27129          callback: yourFunction,
27130          scope: yourObject, //(optional scope)
27131          discardUrl: false,
27132          nocache: false,
27133          text: "Loading...",
27134          timeout: 30,
27135          scripts: false
27136      });
27137      </code></pre>
27138      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27139      * 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.
27140      * @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}
27141      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27142      * @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.
27143      */
27144     load : function(){
27145         var um = this.el.getUpdateManager();
27146         um.update.apply(um, arguments);
27147     },
27148
27149     // note - render is a standard framework call...
27150     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27151     render : function(el, response){
27152         
27153         this.clearSelections();
27154         this.el.update("");
27155         var o;
27156         try{
27157             if (response != '') {
27158                 o = Roo.util.JSON.decode(response.responseText);
27159                 if(this.jsonRoot){
27160                     
27161                     o = o[this.jsonRoot];
27162                 }
27163             }
27164         } catch(e){
27165         }
27166         /**
27167          * The current JSON data or null
27168          */
27169         this.jsonData = o;
27170         this.beforeRender();
27171         this.refresh();
27172     },
27173
27174 /**
27175  * Get the number of records in the current JSON dataset
27176  * @return {Number}
27177  */
27178     getCount : function(){
27179         return this.jsonData ? this.jsonData.length : 0;
27180     },
27181
27182 /**
27183  * Returns the JSON object for the specified node(s)
27184  * @param {HTMLElement/Array} node The node or an array of nodes
27185  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27186  * you get the JSON object for the node
27187  */
27188     getNodeData : function(node){
27189         if(node instanceof Array){
27190             var data = [];
27191             for(var i = 0, len = node.length; i < len; i++){
27192                 data.push(this.getNodeData(node[i]));
27193             }
27194             return data;
27195         }
27196         return this.jsonData[this.indexOf(node)] || null;
27197     },
27198
27199     beforeRender : function(){
27200         this.snapshot = this.jsonData;
27201         if(this.sortInfo){
27202             this.sort.apply(this, this.sortInfo);
27203         }
27204         this.fireEvent("beforerender", this, this.jsonData);
27205     },
27206
27207     onLoad : function(el, o){
27208         this.fireEvent("load", this, this.jsonData, o);
27209     },
27210
27211     onLoadException : function(el, o){
27212         this.fireEvent("loadexception", this, o);
27213     },
27214
27215 /**
27216  * Filter the data by a specific property.
27217  * @param {String} property A property on your JSON objects
27218  * @param {String/RegExp} value Either string that the property values
27219  * should start with, or a RegExp to test against the property
27220  */
27221     filter : function(property, value){
27222         if(this.jsonData){
27223             var data = [];
27224             var ss = this.snapshot;
27225             if(typeof value == "string"){
27226                 var vlen = value.length;
27227                 if(vlen == 0){
27228                     this.clearFilter();
27229                     return;
27230                 }
27231                 value = value.toLowerCase();
27232                 for(var i = 0, len = ss.length; i < len; i++){
27233                     var o = ss[i];
27234                     if(o[property].substr(0, vlen).toLowerCase() == value){
27235                         data.push(o);
27236                     }
27237                 }
27238             } else if(value.exec){ // regex?
27239                 for(var i = 0, len = ss.length; i < len; i++){
27240                     var o = ss[i];
27241                     if(value.test(o[property])){
27242                         data.push(o);
27243                     }
27244                 }
27245             } else{
27246                 return;
27247             }
27248             this.jsonData = data;
27249             this.refresh();
27250         }
27251     },
27252
27253 /**
27254  * Filter by a function. The passed function will be called with each
27255  * object in the current dataset. If the function returns true the value is kept,
27256  * otherwise it is filtered.
27257  * @param {Function} fn
27258  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27259  */
27260     filterBy : function(fn, scope){
27261         if(this.jsonData){
27262             var data = [];
27263             var ss = this.snapshot;
27264             for(var i = 0, len = ss.length; i < len; i++){
27265                 var o = ss[i];
27266                 if(fn.call(scope || this, o)){
27267                     data.push(o);
27268                 }
27269             }
27270             this.jsonData = data;
27271             this.refresh();
27272         }
27273     },
27274
27275 /**
27276  * Clears the current filter.
27277  */
27278     clearFilter : function(){
27279         if(this.snapshot && this.jsonData != this.snapshot){
27280             this.jsonData = this.snapshot;
27281             this.refresh();
27282         }
27283     },
27284
27285
27286 /**
27287  * Sorts the data for this view and refreshes it.
27288  * @param {String} property A property on your JSON objects to sort on
27289  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27290  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27291  */
27292     sort : function(property, dir, sortType){
27293         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27294         if(this.jsonData){
27295             var p = property;
27296             var dsc = dir && dir.toLowerCase() == "desc";
27297             var f = function(o1, o2){
27298                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27299                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27300                 ;
27301                 if(v1 < v2){
27302                     return dsc ? +1 : -1;
27303                 } else if(v1 > v2){
27304                     return dsc ? -1 : +1;
27305                 } else{
27306                     return 0;
27307                 }
27308             };
27309             this.jsonData.sort(f);
27310             this.refresh();
27311             if(this.jsonData != this.snapshot){
27312                 this.snapshot.sort(f);
27313             }
27314         }
27315     }
27316 });/*
27317  * Based on:
27318  * Ext JS Library 1.1.1
27319  * Copyright(c) 2006-2007, Ext JS, LLC.
27320  *
27321  * Originally Released Under LGPL - original licence link has changed is not relivant.
27322  *
27323  * Fork - LGPL
27324  * <script type="text/javascript">
27325  */
27326  
27327
27328 /**
27329  * @class Roo.ColorPalette
27330  * @extends Roo.Component
27331  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27332  * Here's an example of typical usage:
27333  * <pre><code>
27334 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27335 cp.render('my-div');
27336
27337 cp.on('select', function(palette, selColor){
27338     // do something with selColor
27339 });
27340 </code></pre>
27341  * @constructor
27342  * Create a new ColorPalette
27343  * @param {Object} config The config object
27344  */
27345 Roo.ColorPalette = function(config){
27346     Roo.ColorPalette.superclass.constructor.call(this, config);
27347     this.addEvents({
27348         /**
27349              * @event select
27350              * Fires when a color is selected
27351              * @param {ColorPalette} this
27352              * @param {String} color The 6-digit color hex code (without the # symbol)
27353              */
27354         select: true
27355     });
27356
27357     if(this.handler){
27358         this.on("select", this.handler, this.scope, true);
27359     }
27360 };
27361 Roo.extend(Roo.ColorPalette, Roo.Component, {
27362     /**
27363      * @cfg {String} itemCls
27364      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27365      */
27366     itemCls : "x-color-palette",
27367     /**
27368      * @cfg {String} value
27369      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27370      * the hex codes are case-sensitive.
27371      */
27372     value : null,
27373     clickEvent:'click',
27374     // private
27375     ctype: "Roo.ColorPalette",
27376
27377     /**
27378      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27379      */
27380     allowReselect : false,
27381
27382     /**
27383      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27384      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27385      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27386      * of colors with the width setting until the box is symmetrical.</p>
27387      * <p>You can override individual colors if needed:</p>
27388      * <pre><code>
27389 var cp = new Roo.ColorPalette();
27390 cp.colors[0] = "FF0000";  // change the first box to red
27391 </code></pre>
27392
27393 Or you can provide a custom array of your own for complete control:
27394 <pre><code>
27395 var cp = new Roo.ColorPalette();
27396 cp.colors = ["000000", "993300", "333300"];
27397 </code></pre>
27398      * @type Array
27399      */
27400     colors : [
27401         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27402         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27403         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27404         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27405         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27406     ],
27407
27408     // private
27409     onRender : function(container, position){
27410         var t = new Roo.MasterTemplate(
27411             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27412         );
27413         var c = this.colors;
27414         for(var i = 0, len = c.length; i < len; i++){
27415             t.add([c[i]]);
27416         }
27417         var el = document.createElement("div");
27418         el.className = this.itemCls;
27419         t.overwrite(el);
27420         container.dom.insertBefore(el, position);
27421         this.el = Roo.get(el);
27422         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27423         if(this.clickEvent != 'click'){
27424             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27425         }
27426     },
27427
27428     // private
27429     afterRender : function(){
27430         Roo.ColorPalette.superclass.afterRender.call(this);
27431         if(this.value){
27432             var s = this.value;
27433             this.value = null;
27434             this.select(s);
27435         }
27436     },
27437
27438     // private
27439     handleClick : function(e, t){
27440         e.preventDefault();
27441         if(!this.disabled){
27442             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27443             this.select(c.toUpperCase());
27444         }
27445     },
27446
27447     /**
27448      * Selects the specified color in the palette (fires the select event)
27449      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27450      */
27451     select : function(color){
27452         color = color.replace("#", "");
27453         if(color != this.value || this.allowReselect){
27454             var el = this.el;
27455             if(this.value){
27456                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27457             }
27458             el.child("a.color-"+color).addClass("x-color-palette-sel");
27459             this.value = color;
27460             this.fireEvent("select", this, color);
27461         }
27462     }
27463 });/*
27464  * Based on:
27465  * Ext JS Library 1.1.1
27466  * Copyright(c) 2006-2007, Ext JS, LLC.
27467  *
27468  * Originally Released Under LGPL - original licence link has changed is not relivant.
27469  *
27470  * Fork - LGPL
27471  * <script type="text/javascript">
27472  */
27473  
27474 /**
27475  * @class Roo.DatePicker
27476  * @extends Roo.Component
27477  * Simple date picker class.
27478  * @constructor
27479  * Create a new DatePicker
27480  * @param {Object} config The config object
27481  */
27482 Roo.DatePicker = function(config){
27483     Roo.DatePicker.superclass.constructor.call(this, config);
27484
27485     this.value = config && config.value ?
27486                  config.value.clearTime() : new Date().clearTime();
27487
27488     this.addEvents({
27489         /**
27490              * @event select
27491              * Fires when a date is selected
27492              * @param {DatePicker} this
27493              * @param {Date} date The selected date
27494              */
27495         'select': true,
27496         /**
27497              * @event monthchange
27498              * Fires when the displayed month changes 
27499              * @param {DatePicker} this
27500              * @param {Date} date The selected month
27501              */
27502         'monthchange': true
27503     });
27504
27505     if(this.handler){
27506         this.on("select", this.handler,  this.scope || this);
27507     }
27508     // build the disabledDatesRE
27509     if(!this.disabledDatesRE && this.disabledDates){
27510         var dd = this.disabledDates;
27511         var re = "(?:";
27512         for(var i = 0; i < dd.length; i++){
27513             re += dd[i];
27514             if(i != dd.length-1) {
27515                 re += "|";
27516             }
27517         }
27518         this.disabledDatesRE = new RegExp(re + ")");
27519     }
27520 };
27521
27522 Roo.extend(Roo.DatePicker, Roo.Component, {
27523     /**
27524      * @cfg {String} todayText
27525      * The text to display on the button that selects the current date (defaults to "Today")
27526      */
27527     todayText : "Today",
27528     /**
27529      * @cfg {String} okText
27530      * The text to display on the ok button
27531      */
27532     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27533     /**
27534      * @cfg {String} cancelText
27535      * The text to display on the cancel button
27536      */
27537     cancelText : "Cancel",
27538     /**
27539      * @cfg {String} todayTip
27540      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27541      */
27542     todayTip : "{0} (Spacebar)",
27543     /**
27544      * @cfg {Date} minDate
27545      * Minimum allowable date (JavaScript date object, defaults to null)
27546      */
27547     minDate : null,
27548     /**
27549      * @cfg {Date} maxDate
27550      * Maximum allowable date (JavaScript date object, defaults to null)
27551      */
27552     maxDate : null,
27553     /**
27554      * @cfg {String} minText
27555      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27556      */
27557     minText : "This date is before the minimum date",
27558     /**
27559      * @cfg {String} maxText
27560      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27561      */
27562     maxText : "This date is after the maximum date",
27563     /**
27564      * @cfg {String} format
27565      * The default date format string which can be overriden for localization support.  The format must be
27566      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27567      */
27568     format : "m/d/y",
27569     /**
27570      * @cfg {Array} disabledDays
27571      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27572      */
27573     disabledDays : null,
27574     /**
27575      * @cfg {String} disabledDaysText
27576      * The tooltip to display when the date falls on a disabled day (defaults to "")
27577      */
27578     disabledDaysText : "",
27579     /**
27580      * @cfg {RegExp} disabledDatesRE
27581      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27582      */
27583     disabledDatesRE : null,
27584     /**
27585      * @cfg {String} disabledDatesText
27586      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27587      */
27588     disabledDatesText : "",
27589     /**
27590      * @cfg {Boolean} constrainToViewport
27591      * True to constrain the date picker to the viewport (defaults to true)
27592      */
27593     constrainToViewport : true,
27594     /**
27595      * @cfg {Array} monthNames
27596      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27597      */
27598     monthNames : Date.monthNames,
27599     /**
27600      * @cfg {Array} dayNames
27601      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27602      */
27603     dayNames : Date.dayNames,
27604     /**
27605      * @cfg {String} nextText
27606      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27607      */
27608     nextText: 'Next Month (Control+Right)',
27609     /**
27610      * @cfg {String} prevText
27611      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27612      */
27613     prevText: 'Previous Month (Control+Left)',
27614     /**
27615      * @cfg {String} monthYearText
27616      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27617      */
27618     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27619     /**
27620      * @cfg {Number} startDay
27621      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27622      */
27623     startDay : 0,
27624     /**
27625      * @cfg {Bool} showClear
27626      * Show a clear button (usefull for date form elements that can be blank.)
27627      */
27628     
27629     showClear: false,
27630     
27631     /**
27632      * Sets the value of the date field
27633      * @param {Date} value The date to set
27634      */
27635     setValue : function(value){
27636         var old = this.value;
27637         
27638         if (typeof(value) == 'string') {
27639          
27640             value = Date.parseDate(value, this.format);
27641         }
27642         if (!value) {
27643             value = new Date();
27644         }
27645         
27646         this.value = value.clearTime(true);
27647         if(this.el){
27648             this.update(this.value);
27649         }
27650     },
27651
27652     /**
27653      * Gets the current selected value of the date field
27654      * @return {Date} The selected date
27655      */
27656     getValue : function(){
27657         return this.value;
27658     },
27659
27660     // private
27661     focus : function(){
27662         if(this.el){
27663             this.update(this.activeDate);
27664         }
27665     },
27666
27667     // privateval
27668     onRender : function(container, position){
27669         
27670         var m = [
27671              '<table cellspacing="0">',
27672                 '<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>',
27673                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27674         var dn = this.dayNames;
27675         for(var i = 0; i < 7; i++){
27676             var d = this.startDay+i;
27677             if(d > 6){
27678                 d = d-7;
27679             }
27680             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27681         }
27682         m[m.length] = "</tr></thead><tbody><tr>";
27683         for(var i = 0; i < 42; i++) {
27684             if(i % 7 == 0 && i != 0){
27685                 m[m.length] = "</tr><tr>";
27686             }
27687             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27688         }
27689         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27690             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27691
27692         var el = document.createElement("div");
27693         el.className = "x-date-picker";
27694         el.innerHTML = m.join("");
27695
27696         container.dom.insertBefore(el, position);
27697
27698         this.el = Roo.get(el);
27699         this.eventEl = Roo.get(el.firstChild);
27700
27701         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27702             handler: this.showPrevMonth,
27703             scope: this,
27704             preventDefault:true,
27705             stopDefault:true
27706         });
27707
27708         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27709             handler: this.showNextMonth,
27710             scope: this,
27711             preventDefault:true,
27712             stopDefault:true
27713         });
27714
27715         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27716
27717         this.monthPicker = this.el.down('div.x-date-mp');
27718         this.monthPicker.enableDisplayMode('block');
27719         
27720         var kn = new Roo.KeyNav(this.eventEl, {
27721             "left" : function(e){
27722                 e.ctrlKey ?
27723                     this.showPrevMonth() :
27724                     this.update(this.activeDate.add("d", -1));
27725             },
27726
27727             "right" : function(e){
27728                 e.ctrlKey ?
27729                     this.showNextMonth() :
27730                     this.update(this.activeDate.add("d", 1));
27731             },
27732
27733             "up" : function(e){
27734                 e.ctrlKey ?
27735                     this.showNextYear() :
27736                     this.update(this.activeDate.add("d", -7));
27737             },
27738
27739             "down" : function(e){
27740                 e.ctrlKey ?
27741                     this.showPrevYear() :
27742                     this.update(this.activeDate.add("d", 7));
27743             },
27744
27745             "pageUp" : function(e){
27746                 this.showNextMonth();
27747             },
27748
27749             "pageDown" : function(e){
27750                 this.showPrevMonth();
27751             },
27752
27753             "enter" : function(e){
27754                 e.stopPropagation();
27755                 return true;
27756             },
27757
27758             scope : this
27759         });
27760
27761         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27762
27763         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27764
27765         this.el.unselectable();
27766         
27767         this.cells = this.el.select("table.x-date-inner tbody td");
27768         this.textNodes = this.el.query("table.x-date-inner tbody span");
27769
27770         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27771             text: "&#160;",
27772             tooltip: this.monthYearText
27773         });
27774
27775         this.mbtn.on('click', this.showMonthPicker, this);
27776         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27777
27778
27779         var today = (new Date()).dateFormat(this.format);
27780         
27781         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27782         if (this.showClear) {
27783             baseTb.add( new Roo.Toolbar.Fill());
27784         }
27785         baseTb.add({
27786             text: String.format(this.todayText, today),
27787             tooltip: String.format(this.todayTip, today),
27788             handler: this.selectToday,
27789             scope: this
27790         });
27791         
27792         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27793             
27794         //});
27795         if (this.showClear) {
27796             
27797             baseTb.add( new Roo.Toolbar.Fill());
27798             baseTb.add({
27799                 text: '&#160;',
27800                 cls: 'x-btn-icon x-btn-clear',
27801                 handler: function() {
27802                     //this.value = '';
27803                     this.fireEvent("select", this, '');
27804                 },
27805                 scope: this
27806             });
27807         }
27808         
27809         
27810         if(Roo.isIE){
27811             this.el.repaint();
27812         }
27813         this.update(this.value);
27814     },
27815
27816     createMonthPicker : function(){
27817         if(!this.monthPicker.dom.firstChild){
27818             var buf = ['<table border="0" cellspacing="0">'];
27819             for(var i = 0; i < 6; i++){
27820                 buf.push(
27821                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27822                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27823                     i == 0 ?
27824                     '<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>' :
27825                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27826                 );
27827             }
27828             buf.push(
27829                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27830                     this.okText,
27831                     '</button><button type="button" class="x-date-mp-cancel">',
27832                     this.cancelText,
27833                     '</button></td></tr>',
27834                 '</table>'
27835             );
27836             this.monthPicker.update(buf.join(''));
27837             this.monthPicker.on('click', this.onMonthClick, this);
27838             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27839
27840             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27841             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27842
27843             this.mpMonths.each(function(m, a, i){
27844                 i += 1;
27845                 if((i%2) == 0){
27846                     m.dom.xmonth = 5 + Math.round(i * .5);
27847                 }else{
27848                     m.dom.xmonth = Math.round((i-1) * .5);
27849                 }
27850             });
27851         }
27852     },
27853
27854     showMonthPicker : function(){
27855         this.createMonthPicker();
27856         var size = this.el.getSize();
27857         this.monthPicker.setSize(size);
27858         this.monthPicker.child('table').setSize(size);
27859
27860         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27861         this.updateMPMonth(this.mpSelMonth);
27862         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27863         this.updateMPYear(this.mpSelYear);
27864
27865         this.monthPicker.slideIn('t', {duration:.2});
27866     },
27867
27868     updateMPYear : function(y){
27869         this.mpyear = y;
27870         var ys = this.mpYears.elements;
27871         for(var i = 1; i <= 10; i++){
27872             var td = ys[i-1], y2;
27873             if((i%2) == 0){
27874                 y2 = y + Math.round(i * .5);
27875                 td.firstChild.innerHTML = y2;
27876                 td.xyear = y2;
27877             }else{
27878                 y2 = y - (5-Math.round(i * .5));
27879                 td.firstChild.innerHTML = y2;
27880                 td.xyear = y2;
27881             }
27882             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27883         }
27884     },
27885
27886     updateMPMonth : function(sm){
27887         this.mpMonths.each(function(m, a, i){
27888             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27889         });
27890     },
27891
27892     selectMPMonth: function(m){
27893         
27894     },
27895
27896     onMonthClick : function(e, t){
27897         e.stopEvent();
27898         var el = new Roo.Element(t), pn;
27899         if(el.is('button.x-date-mp-cancel')){
27900             this.hideMonthPicker();
27901         }
27902         else if(el.is('button.x-date-mp-ok')){
27903             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27904             this.hideMonthPicker();
27905         }
27906         else if(pn = el.up('td.x-date-mp-month', 2)){
27907             this.mpMonths.removeClass('x-date-mp-sel');
27908             pn.addClass('x-date-mp-sel');
27909             this.mpSelMonth = pn.dom.xmonth;
27910         }
27911         else if(pn = el.up('td.x-date-mp-year', 2)){
27912             this.mpYears.removeClass('x-date-mp-sel');
27913             pn.addClass('x-date-mp-sel');
27914             this.mpSelYear = pn.dom.xyear;
27915         }
27916         else if(el.is('a.x-date-mp-prev')){
27917             this.updateMPYear(this.mpyear-10);
27918         }
27919         else if(el.is('a.x-date-mp-next')){
27920             this.updateMPYear(this.mpyear+10);
27921         }
27922     },
27923
27924     onMonthDblClick : function(e, t){
27925         e.stopEvent();
27926         var el = new Roo.Element(t), pn;
27927         if(pn = el.up('td.x-date-mp-month', 2)){
27928             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27929             this.hideMonthPicker();
27930         }
27931         else if(pn = el.up('td.x-date-mp-year', 2)){
27932             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27933             this.hideMonthPicker();
27934         }
27935     },
27936
27937     hideMonthPicker : function(disableAnim){
27938         if(this.monthPicker){
27939             if(disableAnim === true){
27940                 this.monthPicker.hide();
27941             }else{
27942                 this.monthPicker.slideOut('t', {duration:.2});
27943             }
27944         }
27945     },
27946
27947     // private
27948     showPrevMonth : function(e){
27949         this.update(this.activeDate.add("mo", -1));
27950     },
27951
27952     // private
27953     showNextMonth : function(e){
27954         this.update(this.activeDate.add("mo", 1));
27955     },
27956
27957     // private
27958     showPrevYear : function(){
27959         this.update(this.activeDate.add("y", -1));
27960     },
27961
27962     // private
27963     showNextYear : function(){
27964         this.update(this.activeDate.add("y", 1));
27965     },
27966
27967     // private
27968     handleMouseWheel : function(e){
27969         var delta = e.getWheelDelta();
27970         if(delta > 0){
27971             this.showPrevMonth();
27972             e.stopEvent();
27973         } else if(delta < 0){
27974             this.showNextMonth();
27975             e.stopEvent();
27976         }
27977     },
27978
27979     // private
27980     handleDateClick : function(e, t){
27981         e.stopEvent();
27982         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
27983             this.setValue(new Date(t.dateValue));
27984             this.fireEvent("select", this, this.value);
27985         }
27986     },
27987
27988     // private
27989     selectToday : function(){
27990         this.setValue(new Date().clearTime());
27991         this.fireEvent("select", this, this.value);
27992     },
27993
27994     // private
27995     update : function(date)
27996     {
27997         var vd = this.activeDate;
27998         this.activeDate = date;
27999         if(vd && this.el){
28000             var t = date.getTime();
28001             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28002                 this.cells.removeClass("x-date-selected");
28003                 this.cells.each(function(c){
28004                    if(c.dom.firstChild.dateValue == t){
28005                        c.addClass("x-date-selected");
28006                        setTimeout(function(){
28007                             try{c.dom.firstChild.focus();}catch(e){}
28008                        }, 50);
28009                        return false;
28010                    }
28011                 });
28012                 return;
28013             }
28014         }
28015         
28016         var days = date.getDaysInMonth();
28017         var firstOfMonth = date.getFirstDateOfMonth();
28018         var startingPos = firstOfMonth.getDay()-this.startDay;
28019
28020         if(startingPos <= this.startDay){
28021             startingPos += 7;
28022         }
28023
28024         var pm = date.add("mo", -1);
28025         var prevStart = pm.getDaysInMonth()-startingPos;
28026
28027         var cells = this.cells.elements;
28028         var textEls = this.textNodes;
28029         days += startingPos;
28030
28031         // convert everything to numbers so it's fast
28032         var day = 86400000;
28033         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28034         var today = new Date().clearTime().getTime();
28035         var sel = date.clearTime().getTime();
28036         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28037         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28038         var ddMatch = this.disabledDatesRE;
28039         var ddText = this.disabledDatesText;
28040         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28041         var ddaysText = this.disabledDaysText;
28042         var format = this.format;
28043
28044         var setCellClass = function(cal, cell){
28045             cell.title = "";
28046             var t = d.getTime();
28047             cell.firstChild.dateValue = t;
28048             if(t == today){
28049                 cell.className += " x-date-today";
28050                 cell.title = cal.todayText;
28051             }
28052             if(t == sel){
28053                 cell.className += " x-date-selected";
28054                 setTimeout(function(){
28055                     try{cell.firstChild.focus();}catch(e){}
28056                 }, 50);
28057             }
28058             // disabling
28059             if(t < min) {
28060                 cell.className = " x-date-disabled";
28061                 cell.title = cal.minText;
28062                 return;
28063             }
28064             if(t > max) {
28065                 cell.className = " x-date-disabled";
28066                 cell.title = cal.maxText;
28067                 return;
28068             }
28069             if(ddays){
28070                 if(ddays.indexOf(d.getDay()) != -1){
28071                     cell.title = ddaysText;
28072                     cell.className = " x-date-disabled";
28073                 }
28074             }
28075             if(ddMatch && format){
28076                 var fvalue = d.dateFormat(format);
28077                 if(ddMatch.test(fvalue)){
28078                     cell.title = ddText.replace("%0", fvalue);
28079                     cell.className = " x-date-disabled";
28080                 }
28081             }
28082         };
28083
28084         var i = 0;
28085         for(; i < startingPos; i++) {
28086             textEls[i].innerHTML = (++prevStart);
28087             d.setDate(d.getDate()+1);
28088             cells[i].className = "x-date-prevday";
28089             setCellClass(this, cells[i]);
28090         }
28091         for(; i < days; i++){
28092             intDay = i - startingPos + 1;
28093             textEls[i].innerHTML = (intDay);
28094             d.setDate(d.getDate()+1);
28095             cells[i].className = "x-date-active";
28096             setCellClass(this, cells[i]);
28097         }
28098         var extraDays = 0;
28099         for(; i < 42; i++) {
28100              textEls[i].innerHTML = (++extraDays);
28101              d.setDate(d.getDate()+1);
28102              cells[i].className = "x-date-nextday";
28103              setCellClass(this, cells[i]);
28104         }
28105
28106         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28107         this.fireEvent('monthchange', this, date);
28108         
28109         if(!this.internalRender){
28110             var main = this.el.dom.firstChild;
28111             var w = main.offsetWidth;
28112             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28113             Roo.fly(main).setWidth(w);
28114             this.internalRender = true;
28115             // opera does not respect the auto grow header center column
28116             // then, after it gets a width opera refuses to recalculate
28117             // without a second pass
28118             if(Roo.isOpera && !this.secondPass){
28119                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28120                 this.secondPass = true;
28121                 this.update.defer(10, this, [date]);
28122             }
28123         }
28124         
28125         
28126     }
28127 });        /*
28128  * Based on:
28129  * Ext JS Library 1.1.1
28130  * Copyright(c) 2006-2007, Ext JS, LLC.
28131  *
28132  * Originally Released Under LGPL - original licence link has changed is not relivant.
28133  *
28134  * Fork - LGPL
28135  * <script type="text/javascript">
28136  */
28137 /**
28138  * @class Roo.TabPanel
28139  * @extends Roo.util.Observable
28140  * A lightweight tab container.
28141  * <br><br>
28142  * Usage:
28143  * <pre><code>
28144 // basic tabs 1, built from existing content
28145 var tabs = new Roo.TabPanel("tabs1");
28146 tabs.addTab("script", "View Script");
28147 tabs.addTab("markup", "View Markup");
28148 tabs.activate("script");
28149
28150 // more advanced tabs, built from javascript
28151 var jtabs = new Roo.TabPanel("jtabs");
28152 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28153
28154 // set up the UpdateManager
28155 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28156 var updater = tab2.getUpdateManager();
28157 updater.setDefaultUrl("ajax1.htm");
28158 tab2.on('activate', updater.refresh, updater, true);
28159
28160 // Use setUrl for Ajax loading
28161 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28162 tab3.setUrl("ajax2.htm", null, true);
28163
28164 // Disabled tab
28165 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28166 tab4.disable();
28167
28168 jtabs.activate("jtabs-1");
28169  * </code></pre>
28170  * @constructor
28171  * Create a new TabPanel.
28172  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28173  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28174  */
28175 Roo.TabPanel = function(container, config){
28176     /**
28177     * The container element for this TabPanel.
28178     * @type Roo.Element
28179     */
28180     this.el = Roo.get(container, true);
28181     if(config){
28182         if(typeof config == "boolean"){
28183             this.tabPosition = config ? "bottom" : "top";
28184         }else{
28185             Roo.apply(this, config);
28186         }
28187     }
28188     if(this.tabPosition == "bottom"){
28189         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28190         this.el.addClass("x-tabs-bottom");
28191     }
28192     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28193     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28194     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28195     if(Roo.isIE){
28196         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28197     }
28198     if(this.tabPosition != "bottom"){
28199         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28200          * @type Roo.Element
28201          */
28202         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28203         this.el.addClass("x-tabs-top");
28204     }
28205     this.items = [];
28206
28207     this.bodyEl.setStyle("position", "relative");
28208
28209     this.active = null;
28210     this.activateDelegate = this.activate.createDelegate(this);
28211
28212     this.addEvents({
28213         /**
28214          * @event tabchange
28215          * Fires when the active tab changes
28216          * @param {Roo.TabPanel} this
28217          * @param {Roo.TabPanelItem} activePanel The new active tab
28218          */
28219         "tabchange": true,
28220         /**
28221          * @event beforetabchange
28222          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28223          * @param {Roo.TabPanel} this
28224          * @param {Object} e Set cancel to true on this object to cancel the tab change
28225          * @param {Roo.TabPanelItem} tab The tab being changed to
28226          */
28227         "beforetabchange" : true
28228     });
28229
28230     Roo.EventManager.onWindowResize(this.onResize, this);
28231     this.cpad = this.el.getPadding("lr");
28232     this.hiddenCount = 0;
28233
28234
28235     // toolbar on the tabbar support...
28236     if (this.toolbar) {
28237         var tcfg = this.toolbar;
28238         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28239         this.toolbar = new Roo.Toolbar(tcfg);
28240         if (Roo.isSafari) {
28241             var tbl = tcfg.container.child('table', true);
28242             tbl.setAttribute('width', '100%');
28243         }
28244         
28245     }
28246    
28247
28248
28249     Roo.TabPanel.superclass.constructor.call(this);
28250 };
28251
28252 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28253     /*
28254      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28255      */
28256     tabPosition : "top",
28257     /*
28258      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28259      */
28260     currentTabWidth : 0,
28261     /*
28262      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28263      */
28264     minTabWidth : 40,
28265     /*
28266      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28267      */
28268     maxTabWidth : 250,
28269     /*
28270      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28271      */
28272     preferredTabWidth : 175,
28273     /*
28274      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28275      */
28276     resizeTabs : false,
28277     /*
28278      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28279      */
28280     monitorResize : true,
28281     /*
28282      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28283      */
28284     toolbar : false,
28285
28286     /**
28287      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28288      * @param {String} id The id of the div to use <b>or create</b>
28289      * @param {String} text The text for the tab
28290      * @param {String} content (optional) Content to put in the TabPanelItem body
28291      * @param {Boolean} closable (optional) True to create a close icon on the tab
28292      * @return {Roo.TabPanelItem} The created TabPanelItem
28293      */
28294     addTab : function(id, text, content, closable){
28295         var item = new Roo.TabPanelItem(this, id, text, closable);
28296         this.addTabItem(item);
28297         if(content){
28298             item.setContent(content);
28299         }
28300         return item;
28301     },
28302
28303     /**
28304      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28305      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28306      * @return {Roo.TabPanelItem}
28307      */
28308     getTab : function(id){
28309         return this.items[id];
28310     },
28311
28312     /**
28313      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28314      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28315      */
28316     hideTab : function(id){
28317         var t = this.items[id];
28318         if(!t.isHidden()){
28319            t.setHidden(true);
28320            this.hiddenCount++;
28321            this.autoSizeTabs();
28322         }
28323     },
28324
28325     /**
28326      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28327      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28328      */
28329     unhideTab : function(id){
28330         var t = this.items[id];
28331         if(t.isHidden()){
28332            t.setHidden(false);
28333            this.hiddenCount--;
28334            this.autoSizeTabs();
28335         }
28336     },
28337
28338     /**
28339      * Adds an existing {@link Roo.TabPanelItem}.
28340      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28341      */
28342     addTabItem : function(item){
28343         this.items[item.id] = item;
28344         this.items.push(item);
28345         if(this.resizeTabs){
28346            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28347            this.autoSizeTabs();
28348         }else{
28349             item.autoSize();
28350         }
28351     },
28352
28353     /**
28354      * Removes a {@link Roo.TabPanelItem}.
28355      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28356      */
28357     removeTab : function(id){
28358         var items = this.items;
28359         var tab = items[id];
28360         if(!tab) { return; }
28361         var index = items.indexOf(tab);
28362         if(this.active == tab && items.length > 1){
28363             var newTab = this.getNextAvailable(index);
28364             if(newTab) {
28365                 newTab.activate();
28366             }
28367         }
28368         this.stripEl.dom.removeChild(tab.pnode.dom);
28369         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28370             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28371         }
28372         items.splice(index, 1);
28373         delete this.items[tab.id];
28374         tab.fireEvent("close", tab);
28375         tab.purgeListeners();
28376         this.autoSizeTabs();
28377     },
28378
28379     getNextAvailable : function(start){
28380         var items = this.items;
28381         var index = start;
28382         // look for a next tab that will slide over to
28383         // replace the one being removed
28384         while(index < items.length){
28385             var item = items[++index];
28386             if(item && !item.isHidden()){
28387                 return item;
28388             }
28389         }
28390         // if one isn't found select the previous tab (on the left)
28391         index = start;
28392         while(index >= 0){
28393             var item = items[--index];
28394             if(item && !item.isHidden()){
28395                 return item;
28396             }
28397         }
28398         return null;
28399     },
28400
28401     /**
28402      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28403      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28404      */
28405     disableTab : function(id){
28406         var tab = this.items[id];
28407         if(tab && this.active != tab){
28408             tab.disable();
28409         }
28410     },
28411
28412     /**
28413      * Enables a {@link Roo.TabPanelItem} that is disabled.
28414      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28415      */
28416     enableTab : function(id){
28417         var tab = this.items[id];
28418         tab.enable();
28419     },
28420
28421     /**
28422      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28423      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28424      * @return {Roo.TabPanelItem} The TabPanelItem.
28425      */
28426     activate : function(id){
28427         var tab = this.items[id];
28428         if(!tab){
28429             return null;
28430         }
28431         if(tab == this.active || tab.disabled){
28432             return tab;
28433         }
28434         var e = {};
28435         this.fireEvent("beforetabchange", this, e, tab);
28436         if(e.cancel !== true && !tab.disabled){
28437             if(this.active){
28438                 this.active.hide();
28439             }
28440             this.active = this.items[id];
28441             this.active.show();
28442             this.fireEvent("tabchange", this, this.active);
28443         }
28444         return tab;
28445     },
28446
28447     /**
28448      * Gets the active {@link Roo.TabPanelItem}.
28449      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28450      */
28451     getActiveTab : function(){
28452         return this.active;
28453     },
28454
28455     /**
28456      * Updates the tab body element to fit the height of the container element
28457      * for overflow scrolling
28458      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28459      */
28460     syncHeight : function(targetHeight){
28461         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28462         var bm = this.bodyEl.getMargins();
28463         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28464         this.bodyEl.setHeight(newHeight);
28465         return newHeight;
28466     },
28467
28468     onResize : function(){
28469         if(this.monitorResize){
28470             this.autoSizeTabs();
28471         }
28472     },
28473
28474     /**
28475      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28476      */
28477     beginUpdate : function(){
28478         this.updating = true;
28479     },
28480
28481     /**
28482      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28483      */
28484     endUpdate : function(){
28485         this.updating = false;
28486         this.autoSizeTabs();
28487     },
28488
28489     /**
28490      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28491      */
28492     autoSizeTabs : function(){
28493         var count = this.items.length;
28494         var vcount = count - this.hiddenCount;
28495         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28496             return;
28497         }
28498         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28499         var availWidth = Math.floor(w / vcount);
28500         var b = this.stripBody;
28501         if(b.getWidth() > w){
28502             var tabs = this.items;
28503             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28504             if(availWidth < this.minTabWidth){
28505                 /*if(!this.sleft){    // incomplete scrolling code
28506                     this.createScrollButtons();
28507                 }
28508                 this.showScroll();
28509                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28510             }
28511         }else{
28512             if(this.currentTabWidth < this.preferredTabWidth){
28513                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28514             }
28515         }
28516     },
28517
28518     /**
28519      * Returns the number of tabs in this TabPanel.
28520      * @return {Number}
28521      */
28522      getCount : function(){
28523          return this.items.length;
28524      },
28525
28526     /**
28527      * Resizes all the tabs to the passed width
28528      * @param {Number} The new width
28529      */
28530     setTabWidth : function(width){
28531         this.currentTabWidth = width;
28532         for(var i = 0, len = this.items.length; i < len; i++) {
28533                 if(!this.items[i].isHidden()) {
28534                 this.items[i].setWidth(width);
28535             }
28536         }
28537     },
28538
28539     /**
28540      * Destroys this TabPanel
28541      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28542      */
28543     destroy : function(removeEl){
28544         Roo.EventManager.removeResizeListener(this.onResize, this);
28545         for(var i = 0, len = this.items.length; i < len; i++){
28546             this.items[i].purgeListeners();
28547         }
28548         if(removeEl === true){
28549             this.el.update("");
28550             this.el.remove();
28551         }
28552     }
28553 });
28554
28555 /**
28556  * @class Roo.TabPanelItem
28557  * @extends Roo.util.Observable
28558  * Represents an individual item (tab plus body) in a TabPanel.
28559  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28560  * @param {String} id The id of this TabPanelItem
28561  * @param {String} text The text for the tab of this TabPanelItem
28562  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28563  */
28564 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28565     /**
28566      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28567      * @type Roo.TabPanel
28568      */
28569     this.tabPanel = tabPanel;
28570     /**
28571      * The id for this TabPanelItem
28572      * @type String
28573      */
28574     this.id = id;
28575     /** @private */
28576     this.disabled = false;
28577     /** @private */
28578     this.text = text;
28579     /** @private */
28580     this.loaded = false;
28581     this.closable = closable;
28582
28583     /**
28584      * The body element for this TabPanelItem.
28585      * @type Roo.Element
28586      */
28587     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28588     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28589     this.bodyEl.setStyle("display", "block");
28590     this.bodyEl.setStyle("zoom", "1");
28591     this.hideAction();
28592
28593     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28594     /** @private */
28595     this.el = Roo.get(els.el, true);
28596     this.inner = Roo.get(els.inner, true);
28597     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28598     this.pnode = Roo.get(els.el.parentNode, true);
28599     this.el.on("mousedown", this.onTabMouseDown, this);
28600     this.el.on("click", this.onTabClick, this);
28601     /** @private */
28602     if(closable){
28603         var c = Roo.get(els.close, true);
28604         c.dom.title = this.closeText;
28605         c.addClassOnOver("close-over");
28606         c.on("click", this.closeClick, this);
28607      }
28608
28609     this.addEvents({
28610          /**
28611          * @event activate
28612          * Fires when this tab becomes the active tab.
28613          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28614          * @param {Roo.TabPanelItem} this
28615          */
28616         "activate": true,
28617         /**
28618          * @event beforeclose
28619          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28620          * @param {Roo.TabPanelItem} this
28621          * @param {Object} e Set cancel to true on this object to cancel the close.
28622          */
28623         "beforeclose": true,
28624         /**
28625          * @event close
28626          * Fires when this tab is closed.
28627          * @param {Roo.TabPanelItem} this
28628          */
28629          "close": true,
28630         /**
28631          * @event deactivate
28632          * Fires when this tab is no longer the active tab.
28633          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28634          * @param {Roo.TabPanelItem} this
28635          */
28636          "deactivate" : true
28637     });
28638     this.hidden = false;
28639
28640     Roo.TabPanelItem.superclass.constructor.call(this);
28641 };
28642
28643 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28644     purgeListeners : function(){
28645        Roo.util.Observable.prototype.purgeListeners.call(this);
28646        this.el.removeAllListeners();
28647     },
28648     /**
28649      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28650      */
28651     show : function(){
28652         this.pnode.addClass("on");
28653         this.showAction();
28654         if(Roo.isOpera){
28655             this.tabPanel.stripWrap.repaint();
28656         }
28657         this.fireEvent("activate", this.tabPanel, this);
28658     },
28659
28660     /**
28661      * Returns true if this tab is the active tab.
28662      * @return {Boolean}
28663      */
28664     isActive : function(){
28665         return this.tabPanel.getActiveTab() == this;
28666     },
28667
28668     /**
28669      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28670      */
28671     hide : function(){
28672         this.pnode.removeClass("on");
28673         this.hideAction();
28674         this.fireEvent("deactivate", this.tabPanel, this);
28675     },
28676
28677     hideAction : function(){
28678         this.bodyEl.hide();
28679         this.bodyEl.setStyle("position", "absolute");
28680         this.bodyEl.setLeft("-20000px");
28681         this.bodyEl.setTop("-20000px");
28682     },
28683
28684     showAction : function(){
28685         this.bodyEl.setStyle("position", "relative");
28686         this.bodyEl.setTop("");
28687         this.bodyEl.setLeft("");
28688         this.bodyEl.show();
28689     },
28690
28691     /**
28692      * Set the tooltip for the tab.
28693      * @param {String} tooltip The tab's tooltip
28694      */
28695     setTooltip : function(text){
28696         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28697             this.textEl.dom.qtip = text;
28698             this.textEl.dom.removeAttribute('title');
28699         }else{
28700             this.textEl.dom.title = text;
28701         }
28702     },
28703
28704     onTabClick : function(e){
28705         e.preventDefault();
28706         this.tabPanel.activate(this.id);
28707     },
28708
28709     onTabMouseDown : function(e){
28710         e.preventDefault();
28711         this.tabPanel.activate(this.id);
28712     },
28713
28714     getWidth : function(){
28715         return this.inner.getWidth();
28716     },
28717
28718     setWidth : function(width){
28719         var iwidth = width - this.pnode.getPadding("lr");
28720         this.inner.setWidth(iwidth);
28721         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28722         this.pnode.setWidth(width);
28723     },
28724
28725     /**
28726      * Show or hide the tab
28727      * @param {Boolean} hidden True to hide or false to show.
28728      */
28729     setHidden : function(hidden){
28730         this.hidden = hidden;
28731         this.pnode.setStyle("display", hidden ? "none" : "");
28732     },
28733
28734     /**
28735      * Returns true if this tab is "hidden"
28736      * @return {Boolean}
28737      */
28738     isHidden : function(){
28739         return this.hidden;
28740     },
28741
28742     /**
28743      * Returns the text for this tab
28744      * @return {String}
28745      */
28746     getText : function(){
28747         return this.text;
28748     },
28749
28750     autoSize : function(){
28751         //this.el.beginMeasure();
28752         this.textEl.setWidth(1);
28753         /*
28754          *  #2804 [new] Tabs in Roojs
28755          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28756          */
28757         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28758         //this.el.endMeasure();
28759     },
28760
28761     /**
28762      * Sets the text for the tab (Note: this also sets the tooltip text)
28763      * @param {String} text The tab's text and tooltip
28764      */
28765     setText : function(text){
28766         this.text = text;
28767         this.textEl.update(text);
28768         this.setTooltip(text);
28769         if(!this.tabPanel.resizeTabs){
28770             this.autoSize();
28771         }
28772     },
28773     /**
28774      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28775      */
28776     activate : function(){
28777         this.tabPanel.activate(this.id);
28778     },
28779
28780     /**
28781      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28782      */
28783     disable : function(){
28784         if(this.tabPanel.active != this){
28785             this.disabled = true;
28786             this.pnode.addClass("disabled");
28787         }
28788     },
28789
28790     /**
28791      * Enables this TabPanelItem if it was previously disabled.
28792      */
28793     enable : function(){
28794         this.disabled = false;
28795         this.pnode.removeClass("disabled");
28796     },
28797
28798     /**
28799      * Sets the content for this TabPanelItem.
28800      * @param {String} content The content
28801      * @param {Boolean} loadScripts true to look for and load scripts
28802      */
28803     setContent : function(content, loadScripts){
28804         this.bodyEl.update(content, loadScripts);
28805     },
28806
28807     /**
28808      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28809      * @return {Roo.UpdateManager} The UpdateManager
28810      */
28811     getUpdateManager : function(){
28812         return this.bodyEl.getUpdateManager();
28813     },
28814
28815     /**
28816      * Set a URL to be used to load the content for this TabPanelItem.
28817      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28818      * @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)
28819      * @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)
28820      * @return {Roo.UpdateManager} The UpdateManager
28821      */
28822     setUrl : function(url, params, loadOnce){
28823         if(this.refreshDelegate){
28824             this.un('activate', this.refreshDelegate);
28825         }
28826         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28827         this.on("activate", this.refreshDelegate);
28828         return this.bodyEl.getUpdateManager();
28829     },
28830
28831     /** @private */
28832     _handleRefresh : function(url, params, loadOnce){
28833         if(!loadOnce || !this.loaded){
28834             var updater = this.bodyEl.getUpdateManager();
28835             updater.update(url, params, this._setLoaded.createDelegate(this));
28836         }
28837     },
28838
28839     /**
28840      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28841      *   Will fail silently if the setUrl method has not been called.
28842      *   This does not activate the panel, just updates its content.
28843      */
28844     refresh : function(){
28845         if(this.refreshDelegate){
28846            this.loaded = false;
28847            this.refreshDelegate();
28848         }
28849     },
28850
28851     /** @private */
28852     _setLoaded : function(){
28853         this.loaded = true;
28854     },
28855
28856     /** @private */
28857     closeClick : function(e){
28858         var o = {};
28859         e.stopEvent();
28860         this.fireEvent("beforeclose", this, o);
28861         if(o.cancel !== true){
28862             this.tabPanel.removeTab(this.id);
28863         }
28864     },
28865     /**
28866      * The text displayed in the tooltip for the close icon.
28867      * @type String
28868      */
28869     closeText : "Close this tab"
28870 });
28871
28872 /** @private */
28873 Roo.TabPanel.prototype.createStrip = function(container){
28874     var strip = document.createElement("div");
28875     strip.className = "x-tabs-wrap";
28876     container.appendChild(strip);
28877     return strip;
28878 };
28879 /** @private */
28880 Roo.TabPanel.prototype.createStripList = function(strip){
28881     // div wrapper for retard IE
28882     // returns the "tr" element.
28883     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28884         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28885         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28886     return strip.firstChild.firstChild.firstChild.firstChild;
28887 };
28888 /** @private */
28889 Roo.TabPanel.prototype.createBody = function(container){
28890     var body = document.createElement("div");
28891     Roo.id(body, "tab-body");
28892     Roo.fly(body).addClass("x-tabs-body");
28893     container.appendChild(body);
28894     return body;
28895 };
28896 /** @private */
28897 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28898     var body = Roo.getDom(id);
28899     if(!body){
28900         body = document.createElement("div");
28901         body.id = id;
28902     }
28903     Roo.fly(body).addClass("x-tabs-item-body");
28904     bodyEl.insertBefore(body, bodyEl.firstChild);
28905     return body;
28906 };
28907 /** @private */
28908 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28909     var td = document.createElement("td");
28910     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28911     //stripEl.appendChild(td);
28912     if(closable){
28913         td.className = "x-tabs-closable";
28914         if(!this.closeTpl){
28915             this.closeTpl = new Roo.Template(
28916                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28917                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28918                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28919             );
28920         }
28921         var el = this.closeTpl.overwrite(td, {"text": text});
28922         var close = el.getElementsByTagName("div")[0];
28923         var inner = el.getElementsByTagName("em")[0];
28924         return {"el": el, "close": close, "inner": inner};
28925     } else {
28926         if(!this.tabTpl){
28927             this.tabTpl = new Roo.Template(
28928                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28929                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28930             );
28931         }
28932         var el = this.tabTpl.overwrite(td, {"text": text});
28933         var inner = el.getElementsByTagName("em")[0];
28934         return {"el": el, "inner": inner};
28935     }
28936 };/*
28937  * Based on:
28938  * Ext JS Library 1.1.1
28939  * Copyright(c) 2006-2007, Ext JS, LLC.
28940  *
28941  * Originally Released Under LGPL - original licence link has changed is not relivant.
28942  *
28943  * Fork - LGPL
28944  * <script type="text/javascript">
28945  */
28946
28947 /**
28948  * @class Roo.Button
28949  * @extends Roo.util.Observable
28950  * Simple Button class
28951  * @cfg {String} text The button text
28952  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
28953  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
28954  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
28955  * @cfg {Object} scope The scope of the handler
28956  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
28957  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
28958  * @cfg {Boolean} hidden True to start hidden (defaults to false)
28959  * @cfg {Boolean} disabled True to start disabled (defaults to false)
28960  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
28961  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
28962    applies if enableToggle = true)
28963  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
28964  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
28965   an {@link Roo.util.ClickRepeater} config object (defaults to false).
28966  * @constructor
28967  * Create a new button
28968  * @param {Object} config The config object
28969  */
28970 Roo.Button = function(renderTo, config)
28971 {
28972     if (!config) {
28973         config = renderTo;
28974         renderTo = config.renderTo || false;
28975     }
28976     
28977     Roo.apply(this, config);
28978     this.addEvents({
28979         /**
28980              * @event click
28981              * Fires when this button is clicked
28982              * @param {Button} this
28983              * @param {EventObject} e The click event
28984              */
28985             "click" : true,
28986         /**
28987              * @event toggle
28988              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
28989              * @param {Button} this
28990              * @param {Boolean} pressed
28991              */
28992             "toggle" : true,
28993         /**
28994              * @event mouseover
28995              * Fires when the mouse hovers over the button
28996              * @param {Button} this
28997              * @param {Event} e The event object
28998              */
28999         'mouseover' : true,
29000         /**
29001              * @event mouseout
29002              * Fires when the mouse exits the button
29003              * @param {Button} this
29004              * @param {Event} e The event object
29005              */
29006         'mouseout': true,
29007          /**
29008              * @event render
29009              * Fires when the button is rendered
29010              * @param {Button} this
29011              */
29012         'render': true
29013     });
29014     if(this.menu){
29015         this.menu = Roo.menu.MenuMgr.get(this.menu);
29016     }
29017     // register listeners first!!  - so render can be captured..
29018     Roo.util.Observable.call(this);
29019     if(renderTo){
29020         this.render(renderTo);
29021     }
29022     
29023   
29024 };
29025
29026 Roo.extend(Roo.Button, Roo.util.Observable, {
29027     /**
29028      * 
29029      */
29030     
29031     /**
29032      * Read-only. True if this button is hidden
29033      * @type Boolean
29034      */
29035     hidden : false,
29036     /**
29037      * Read-only. True if this button is disabled
29038      * @type Boolean
29039      */
29040     disabled : false,
29041     /**
29042      * Read-only. True if this button is pressed (only if enableToggle = true)
29043      * @type Boolean
29044      */
29045     pressed : false,
29046
29047     /**
29048      * @cfg {Number} tabIndex 
29049      * The DOM tabIndex for this button (defaults to undefined)
29050      */
29051     tabIndex : undefined,
29052
29053     /**
29054      * @cfg {Boolean} enableToggle
29055      * True to enable pressed/not pressed toggling (defaults to false)
29056      */
29057     enableToggle: false,
29058     /**
29059      * @cfg {Mixed} menu
29060      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29061      */
29062     menu : undefined,
29063     /**
29064      * @cfg {String} menuAlign
29065      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29066      */
29067     menuAlign : "tl-bl?",
29068
29069     /**
29070      * @cfg {String} iconCls
29071      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29072      */
29073     iconCls : undefined,
29074     /**
29075      * @cfg {String} type
29076      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29077      */
29078     type : 'button',
29079
29080     // private
29081     menuClassTarget: 'tr',
29082
29083     /**
29084      * @cfg {String} clickEvent
29085      * The type of event to map to the button's event handler (defaults to 'click')
29086      */
29087     clickEvent : 'click',
29088
29089     /**
29090      * @cfg {Boolean} handleMouseEvents
29091      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29092      */
29093     handleMouseEvents : true,
29094
29095     /**
29096      * @cfg {String} tooltipType
29097      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29098      */
29099     tooltipType : 'qtip',
29100
29101     /**
29102      * @cfg {String} cls
29103      * A CSS class to apply to the button's main element.
29104      */
29105     
29106     /**
29107      * @cfg {Roo.Template} template (Optional)
29108      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29109      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29110      * require code modifications if required elements (e.g. a button) aren't present.
29111      */
29112
29113     // private
29114     render : function(renderTo){
29115         var btn;
29116         if(this.hideParent){
29117             this.parentEl = Roo.get(renderTo);
29118         }
29119         if(!this.dhconfig){
29120             if(!this.template){
29121                 if(!Roo.Button.buttonTemplate){
29122                     // hideous table template
29123                     Roo.Button.buttonTemplate = new Roo.Template(
29124                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29125                         '<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>',
29126                         "</tr></tbody></table>");
29127                 }
29128                 this.template = Roo.Button.buttonTemplate;
29129             }
29130             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29131             var btnEl = btn.child("button:first");
29132             btnEl.on('focus', this.onFocus, this);
29133             btnEl.on('blur', this.onBlur, this);
29134             if(this.cls){
29135                 btn.addClass(this.cls);
29136             }
29137             if(this.icon){
29138                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29139             }
29140             if(this.iconCls){
29141                 btnEl.addClass(this.iconCls);
29142                 if(!this.cls){
29143                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29144                 }
29145             }
29146             if(this.tabIndex !== undefined){
29147                 btnEl.dom.tabIndex = this.tabIndex;
29148             }
29149             if(this.tooltip){
29150                 if(typeof this.tooltip == 'object'){
29151                     Roo.QuickTips.tips(Roo.apply({
29152                           target: btnEl.id
29153                     }, this.tooltip));
29154                 } else {
29155                     btnEl.dom[this.tooltipType] = this.tooltip;
29156                 }
29157             }
29158         }else{
29159             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29160         }
29161         this.el = btn;
29162         if(this.id){
29163             this.el.dom.id = this.el.id = this.id;
29164         }
29165         if(this.menu){
29166             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29167             this.menu.on("show", this.onMenuShow, this);
29168             this.menu.on("hide", this.onMenuHide, this);
29169         }
29170         btn.addClass("x-btn");
29171         if(Roo.isIE && !Roo.isIE7){
29172             this.autoWidth.defer(1, this);
29173         }else{
29174             this.autoWidth();
29175         }
29176         if(this.handleMouseEvents){
29177             btn.on("mouseover", this.onMouseOver, this);
29178             btn.on("mouseout", this.onMouseOut, this);
29179             btn.on("mousedown", this.onMouseDown, this);
29180         }
29181         btn.on(this.clickEvent, this.onClick, this);
29182         //btn.on("mouseup", this.onMouseUp, this);
29183         if(this.hidden){
29184             this.hide();
29185         }
29186         if(this.disabled){
29187             this.disable();
29188         }
29189         Roo.ButtonToggleMgr.register(this);
29190         if(this.pressed){
29191             this.el.addClass("x-btn-pressed");
29192         }
29193         if(this.repeat){
29194             var repeater = new Roo.util.ClickRepeater(btn,
29195                 typeof this.repeat == "object" ? this.repeat : {}
29196             );
29197             repeater.on("click", this.onClick,  this);
29198         }
29199         
29200         this.fireEvent('render', this);
29201         
29202     },
29203     /**
29204      * Returns the button's underlying element
29205      * @return {Roo.Element} The element
29206      */
29207     getEl : function(){
29208         return this.el;  
29209     },
29210     
29211     /**
29212      * Destroys this Button and removes any listeners.
29213      */
29214     destroy : function(){
29215         Roo.ButtonToggleMgr.unregister(this);
29216         this.el.removeAllListeners();
29217         this.purgeListeners();
29218         this.el.remove();
29219     },
29220
29221     // private
29222     autoWidth : function(){
29223         if(this.el){
29224             this.el.setWidth("auto");
29225             if(Roo.isIE7 && Roo.isStrict){
29226                 var ib = this.el.child('button');
29227                 if(ib && ib.getWidth() > 20){
29228                     ib.clip();
29229                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29230                 }
29231             }
29232             if(this.minWidth){
29233                 if(this.hidden){
29234                     this.el.beginMeasure();
29235                 }
29236                 if(this.el.getWidth() < this.minWidth){
29237                     this.el.setWidth(this.minWidth);
29238                 }
29239                 if(this.hidden){
29240                     this.el.endMeasure();
29241                 }
29242             }
29243         }
29244     },
29245
29246     /**
29247      * Assigns this button's click handler
29248      * @param {Function} handler The function to call when the button is clicked
29249      * @param {Object} scope (optional) Scope for the function passed in
29250      */
29251     setHandler : function(handler, scope){
29252         this.handler = handler;
29253         this.scope = scope;  
29254     },
29255     
29256     /**
29257      * Sets this button's text
29258      * @param {String} text The button text
29259      */
29260     setText : function(text){
29261         this.text = text;
29262         if(this.el){
29263             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29264         }
29265         this.autoWidth();
29266     },
29267     
29268     /**
29269      * Gets the text for this button
29270      * @return {String} The button text
29271      */
29272     getText : function(){
29273         return this.text;  
29274     },
29275     
29276     /**
29277      * Show this button
29278      */
29279     show: function(){
29280         this.hidden = false;
29281         if(this.el){
29282             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29283         }
29284     },
29285     
29286     /**
29287      * Hide this button
29288      */
29289     hide: function(){
29290         this.hidden = true;
29291         if(this.el){
29292             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29293         }
29294     },
29295     
29296     /**
29297      * Convenience function for boolean show/hide
29298      * @param {Boolean} visible True to show, false to hide
29299      */
29300     setVisible: function(visible){
29301         if(visible) {
29302             this.show();
29303         }else{
29304             this.hide();
29305         }
29306     },
29307     
29308     /**
29309      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29310      * @param {Boolean} state (optional) Force a particular state
29311      */
29312     toggle : function(state){
29313         state = state === undefined ? !this.pressed : state;
29314         if(state != this.pressed){
29315             if(state){
29316                 this.el.addClass("x-btn-pressed");
29317                 this.pressed = true;
29318                 this.fireEvent("toggle", this, true);
29319             }else{
29320                 this.el.removeClass("x-btn-pressed");
29321                 this.pressed = false;
29322                 this.fireEvent("toggle", this, false);
29323             }
29324             if(this.toggleHandler){
29325                 this.toggleHandler.call(this.scope || this, this, state);
29326             }
29327         }
29328     },
29329     
29330     /**
29331      * Focus the button
29332      */
29333     focus : function(){
29334         this.el.child('button:first').focus();
29335     },
29336     
29337     /**
29338      * Disable this button
29339      */
29340     disable : function(){
29341         if(this.el){
29342             this.el.addClass("x-btn-disabled");
29343         }
29344         this.disabled = true;
29345     },
29346     
29347     /**
29348      * Enable this button
29349      */
29350     enable : function(){
29351         if(this.el){
29352             this.el.removeClass("x-btn-disabled");
29353         }
29354         this.disabled = false;
29355     },
29356
29357     /**
29358      * Convenience function for boolean enable/disable
29359      * @param {Boolean} enabled True to enable, false to disable
29360      */
29361     setDisabled : function(v){
29362         this[v !== true ? "enable" : "disable"]();
29363     },
29364
29365     // private
29366     onClick : function(e)
29367     {
29368         if(e){
29369             e.preventDefault();
29370         }
29371         if(e.button != 0){
29372             return;
29373         }
29374         if(!this.disabled){
29375             if(this.enableToggle){
29376                 this.toggle();
29377             }
29378             if(this.menu && !this.menu.isVisible()){
29379                 this.menu.show(this.el, this.menuAlign);
29380             }
29381             this.fireEvent("click", this, e);
29382             if(this.handler){
29383                 this.el.removeClass("x-btn-over");
29384                 this.handler.call(this.scope || this, this, e);
29385             }
29386         }
29387     },
29388     // private
29389     onMouseOver : function(e){
29390         if(!this.disabled){
29391             this.el.addClass("x-btn-over");
29392             this.fireEvent('mouseover', this, e);
29393         }
29394     },
29395     // private
29396     onMouseOut : function(e){
29397         if(!e.within(this.el,  true)){
29398             this.el.removeClass("x-btn-over");
29399             this.fireEvent('mouseout', this, e);
29400         }
29401     },
29402     // private
29403     onFocus : function(e){
29404         if(!this.disabled){
29405             this.el.addClass("x-btn-focus");
29406         }
29407     },
29408     // private
29409     onBlur : function(e){
29410         this.el.removeClass("x-btn-focus");
29411     },
29412     // private
29413     onMouseDown : function(e){
29414         if(!this.disabled && e.button == 0){
29415             this.el.addClass("x-btn-click");
29416             Roo.get(document).on('mouseup', this.onMouseUp, this);
29417         }
29418     },
29419     // private
29420     onMouseUp : function(e){
29421         if(e.button == 0){
29422             this.el.removeClass("x-btn-click");
29423             Roo.get(document).un('mouseup', this.onMouseUp, this);
29424         }
29425     },
29426     // private
29427     onMenuShow : function(e){
29428         this.el.addClass("x-btn-menu-active");
29429     },
29430     // private
29431     onMenuHide : function(e){
29432         this.el.removeClass("x-btn-menu-active");
29433     }   
29434 });
29435
29436 // Private utility class used by Button
29437 Roo.ButtonToggleMgr = function(){
29438    var groups = {};
29439    
29440    function toggleGroup(btn, state){
29441        if(state){
29442            var g = groups[btn.toggleGroup];
29443            for(var i = 0, l = g.length; i < l; i++){
29444                if(g[i] != btn){
29445                    g[i].toggle(false);
29446                }
29447            }
29448        }
29449    }
29450    
29451    return {
29452        register : function(btn){
29453            if(!btn.toggleGroup){
29454                return;
29455            }
29456            var g = groups[btn.toggleGroup];
29457            if(!g){
29458                g = groups[btn.toggleGroup] = [];
29459            }
29460            g.push(btn);
29461            btn.on("toggle", toggleGroup);
29462        },
29463        
29464        unregister : function(btn){
29465            if(!btn.toggleGroup){
29466                return;
29467            }
29468            var g = groups[btn.toggleGroup];
29469            if(g){
29470                g.remove(btn);
29471                btn.un("toggle", toggleGroup);
29472            }
29473        }
29474    };
29475 }();/*
29476  * Based on:
29477  * Ext JS Library 1.1.1
29478  * Copyright(c) 2006-2007, Ext JS, LLC.
29479  *
29480  * Originally Released Under LGPL - original licence link has changed is not relivant.
29481  *
29482  * Fork - LGPL
29483  * <script type="text/javascript">
29484  */
29485  
29486 /**
29487  * @class Roo.SplitButton
29488  * @extends Roo.Button
29489  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29490  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29491  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29492  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29493  * @cfg {String} arrowTooltip The title attribute of the arrow
29494  * @constructor
29495  * Create a new menu button
29496  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29497  * @param {Object} config The config object
29498  */
29499 Roo.SplitButton = function(renderTo, config){
29500     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29501     /**
29502      * @event arrowclick
29503      * Fires when this button's arrow is clicked
29504      * @param {SplitButton} this
29505      * @param {EventObject} e The click event
29506      */
29507     this.addEvents({"arrowclick":true});
29508 };
29509
29510 Roo.extend(Roo.SplitButton, Roo.Button, {
29511     render : function(renderTo){
29512         // this is one sweet looking template!
29513         var tpl = new Roo.Template(
29514             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29515             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29516             '<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>',
29517             "</tbody></table></td><td>",
29518             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29519             '<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>',
29520             "</tbody></table></td></tr></table>"
29521         );
29522         var btn = tpl.append(renderTo, [this.text, this.type], true);
29523         var btnEl = btn.child("button");
29524         if(this.cls){
29525             btn.addClass(this.cls);
29526         }
29527         if(this.icon){
29528             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29529         }
29530         if(this.iconCls){
29531             btnEl.addClass(this.iconCls);
29532             if(!this.cls){
29533                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29534             }
29535         }
29536         this.el = btn;
29537         if(this.handleMouseEvents){
29538             btn.on("mouseover", this.onMouseOver, this);
29539             btn.on("mouseout", this.onMouseOut, this);
29540             btn.on("mousedown", this.onMouseDown, this);
29541             btn.on("mouseup", this.onMouseUp, this);
29542         }
29543         btn.on(this.clickEvent, this.onClick, this);
29544         if(this.tooltip){
29545             if(typeof this.tooltip == 'object'){
29546                 Roo.QuickTips.tips(Roo.apply({
29547                       target: btnEl.id
29548                 }, this.tooltip));
29549             } else {
29550                 btnEl.dom[this.tooltipType] = this.tooltip;
29551             }
29552         }
29553         if(this.arrowTooltip){
29554             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29555         }
29556         if(this.hidden){
29557             this.hide();
29558         }
29559         if(this.disabled){
29560             this.disable();
29561         }
29562         if(this.pressed){
29563             this.el.addClass("x-btn-pressed");
29564         }
29565         if(Roo.isIE && !Roo.isIE7){
29566             this.autoWidth.defer(1, this);
29567         }else{
29568             this.autoWidth();
29569         }
29570         if(this.menu){
29571             this.menu.on("show", this.onMenuShow, this);
29572             this.menu.on("hide", this.onMenuHide, this);
29573         }
29574         this.fireEvent('render', this);
29575     },
29576
29577     // private
29578     autoWidth : function(){
29579         if(this.el){
29580             var tbl = this.el.child("table:first");
29581             var tbl2 = this.el.child("table:last");
29582             this.el.setWidth("auto");
29583             tbl.setWidth("auto");
29584             if(Roo.isIE7 && Roo.isStrict){
29585                 var ib = this.el.child('button:first');
29586                 if(ib && ib.getWidth() > 20){
29587                     ib.clip();
29588                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29589                 }
29590             }
29591             if(this.minWidth){
29592                 if(this.hidden){
29593                     this.el.beginMeasure();
29594                 }
29595                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29596                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29597                 }
29598                 if(this.hidden){
29599                     this.el.endMeasure();
29600                 }
29601             }
29602             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29603         } 
29604     },
29605     /**
29606      * Sets this button's click handler
29607      * @param {Function} handler The function to call when the button is clicked
29608      * @param {Object} scope (optional) Scope for the function passed above
29609      */
29610     setHandler : function(handler, scope){
29611         this.handler = handler;
29612         this.scope = scope;  
29613     },
29614     
29615     /**
29616      * Sets this button's arrow click handler
29617      * @param {Function} handler The function to call when the arrow is clicked
29618      * @param {Object} scope (optional) Scope for the function passed above
29619      */
29620     setArrowHandler : function(handler, scope){
29621         this.arrowHandler = handler;
29622         this.scope = scope;  
29623     },
29624     
29625     /**
29626      * Focus the button
29627      */
29628     focus : function(){
29629         if(this.el){
29630             this.el.child("button:first").focus();
29631         }
29632     },
29633
29634     // private
29635     onClick : function(e){
29636         e.preventDefault();
29637         if(!this.disabled){
29638             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29639                 if(this.menu && !this.menu.isVisible()){
29640                     this.menu.show(this.el, this.menuAlign);
29641                 }
29642                 this.fireEvent("arrowclick", this, e);
29643                 if(this.arrowHandler){
29644                     this.arrowHandler.call(this.scope || this, this, e);
29645                 }
29646             }else{
29647                 this.fireEvent("click", this, e);
29648                 if(this.handler){
29649                     this.handler.call(this.scope || this, this, e);
29650                 }
29651             }
29652         }
29653     },
29654     // private
29655     onMouseDown : function(e){
29656         if(!this.disabled){
29657             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29658         }
29659     },
29660     // private
29661     onMouseUp : function(e){
29662         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29663     }   
29664 });
29665
29666
29667 // backwards compat
29668 Roo.MenuButton = Roo.SplitButton;/*
29669  * Based on:
29670  * Ext JS Library 1.1.1
29671  * Copyright(c) 2006-2007, Ext JS, LLC.
29672  *
29673  * Originally Released Under LGPL - original licence link has changed is not relivant.
29674  *
29675  * Fork - LGPL
29676  * <script type="text/javascript">
29677  */
29678
29679 /**
29680  * @class Roo.Toolbar
29681  * Basic Toolbar class.
29682  * @constructor
29683  * Creates a new Toolbar
29684  * @param {Object} container The config object
29685  */ 
29686 Roo.Toolbar = function(container, buttons, config)
29687 {
29688     /// old consturctor format still supported..
29689     if(container instanceof Array){ // omit the container for later rendering
29690         buttons = container;
29691         config = buttons;
29692         container = null;
29693     }
29694     if (typeof(container) == 'object' && container.xtype) {
29695         config = container;
29696         container = config.container;
29697         buttons = config.buttons || []; // not really - use items!!
29698     }
29699     var xitems = [];
29700     if (config && config.items) {
29701         xitems = config.items;
29702         delete config.items;
29703     }
29704     Roo.apply(this, config);
29705     this.buttons = buttons;
29706     
29707     if(container){
29708         this.render(container);
29709     }
29710     this.xitems = xitems;
29711     Roo.each(xitems, function(b) {
29712         this.add(b);
29713     }, this);
29714     
29715 };
29716
29717 Roo.Toolbar.prototype = {
29718     /**
29719      * @cfg {Array} items
29720      * array of button configs or elements to add (will be converted to a MixedCollection)
29721      */
29722     
29723     /**
29724      * @cfg {String/HTMLElement/Element} container
29725      * The id or element that will contain the toolbar
29726      */
29727     // private
29728     render : function(ct){
29729         this.el = Roo.get(ct);
29730         if(this.cls){
29731             this.el.addClass(this.cls);
29732         }
29733         // using a table allows for vertical alignment
29734         // 100% width is needed by Safari...
29735         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29736         this.tr = this.el.child("tr", true);
29737         var autoId = 0;
29738         this.items = new Roo.util.MixedCollection(false, function(o){
29739             return o.id || ("item" + (++autoId));
29740         });
29741         if(this.buttons){
29742             this.add.apply(this, this.buttons);
29743             delete this.buttons;
29744         }
29745     },
29746
29747     /**
29748      * Adds element(s) to the toolbar -- this function takes a variable number of 
29749      * arguments of mixed type and adds them to the toolbar.
29750      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29751      * <ul>
29752      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29753      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29754      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29755      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29756      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29757      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29758      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29759      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29760      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29761      * </ul>
29762      * @param {Mixed} arg2
29763      * @param {Mixed} etc.
29764      */
29765     add : function(){
29766         var a = arguments, l = a.length;
29767         for(var i = 0; i < l; i++){
29768             this._add(a[i]);
29769         }
29770     },
29771     // private..
29772     _add : function(el) {
29773         
29774         if (el.xtype) {
29775             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29776         }
29777         
29778         if (el.applyTo){ // some kind of form field
29779             return this.addField(el);
29780         } 
29781         if (el.render){ // some kind of Toolbar.Item
29782             return this.addItem(el);
29783         }
29784         if (typeof el == "string"){ // string
29785             if(el == "separator" || el == "-"){
29786                 return this.addSeparator();
29787             }
29788             if (el == " "){
29789                 return this.addSpacer();
29790             }
29791             if(el == "->"){
29792                 return this.addFill();
29793             }
29794             return this.addText(el);
29795             
29796         }
29797         if(el.tagName){ // element
29798             return this.addElement(el);
29799         }
29800         if(typeof el == "object"){ // must be button config?
29801             return this.addButton(el);
29802         }
29803         // and now what?!?!
29804         return false;
29805         
29806     },
29807     
29808     /**
29809      * Add an Xtype element
29810      * @param {Object} xtype Xtype Object
29811      * @return {Object} created Object
29812      */
29813     addxtype : function(e){
29814         return this.add(e);  
29815     },
29816     
29817     /**
29818      * Returns the Element for this toolbar.
29819      * @return {Roo.Element}
29820      */
29821     getEl : function(){
29822         return this.el;  
29823     },
29824     
29825     /**
29826      * Adds a separator
29827      * @return {Roo.Toolbar.Item} The separator item
29828      */
29829     addSeparator : function(){
29830         return this.addItem(new Roo.Toolbar.Separator());
29831     },
29832
29833     /**
29834      * Adds a spacer element
29835      * @return {Roo.Toolbar.Spacer} The spacer item
29836      */
29837     addSpacer : function(){
29838         return this.addItem(new Roo.Toolbar.Spacer());
29839     },
29840
29841     /**
29842      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29843      * @return {Roo.Toolbar.Fill} The fill item
29844      */
29845     addFill : function(){
29846         return this.addItem(new Roo.Toolbar.Fill());
29847     },
29848
29849     /**
29850      * Adds any standard HTML element to the toolbar
29851      * @param {String/HTMLElement/Element} el The element or id of the element to add
29852      * @return {Roo.Toolbar.Item} The element's item
29853      */
29854     addElement : function(el){
29855         return this.addItem(new Roo.Toolbar.Item(el));
29856     },
29857     /**
29858      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29859      * @type Roo.util.MixedCollection  
29860      */
29861     items : false,
29862      
29863     /**
29864      * Adds any Toolbar.Item or subclass
29865      * @param {Roo.Toolbar.Item} item
29866      * @return {Roo.Toolbar.Item} The item
29867      */
29868     addItem : function(item){
29869         var td = this.nextBlock();
29870         item.render(td);
29871         this.items.add(item);
29872         return item;
29873     },
29874     
29875     /**
29876      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29877      * @param {Object/Array} config A button config or array of configs
29878      * @return {Roo.Toolbar.Button/Array}
29879      */
29880     addButton : function(config){
29881         if(config instanceof Array){
29882             var buttons = [];
29883             for(var i = 0, len = config.length; i < len; i++) {
29884                 buttons.push(this.addButton(config[i]));
29885             }
29886             return buttons;
29887         }
29888         var b = config;
29889         if(!(config instanceof Roo.Toolbar.Button)){
29890             b = config.split ?
29891                 new Roo.Toolbar.SplitButton(config) :
29892                 new Roo.Toolbar.Button(config);
29893         }
29894         var td = this.nextBlock();
29895         b.render(td);
29896         this.items.add(b);
29897         return b;
29898     },
29899     
29900     /**
29901      * Adds text to the toolbar
29902      * @param {String} text The text to add
29903      * @return {Roo.Toolbar.Item} The element's item
29904      */
29905     addText : function(text){
29906         return this.addItem(new Roo.Toolbar.TextItem(text));
29907     },
29908     
29909     /**
29910      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29911      * @param {Number} index The index where the item is to be inserted
29912      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29913      * @return {Roo.Toolbar.Button/Item}
29914      */
29915     insertButton : function(index, item){
29916         if(item instanceof Array){
29917             var buttons = [];
29918             for(var i = 0, len = item.length; i < len; i++) {
29919                buttons.push(this.insertButton(index + i, item[i]));
29920             }
29921             return buttons;
29922         }
29923         if (!(item instanceof Roo.Toolbar.Button)){
29924            item = new Roo.Toolbar.Button(item);
29925         }
29926         var td = document.createElement("td");
29927         this.tr.insertBefore(td, this.tr.childNodes[index]);
29928         item.render(td);
29929         this.items.insert(index, item);
29930         return item;
29931     },
29932     
29933     /**
29934      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29935      * @param {Object} config
29936      * @return {Roo.Toolbar.Item} The element's item
29937      */
29938     addDom : function(config, returnEl){
29939         var td = this.nextBlock();
29940         Roo.DomHelper.overwrite(td, config);
29941         var ti = new Roo.Toolbar.Item(td.firstChild);
29942         ti.render(td);
29943         this.items.add(ti);
29944         return ti;
29945     },
29946
29947     /**
29948      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
29949      * @type Roo.util.MixedCollection  
29950      */
29951     fields : false,
29952     
29953     /**
29954      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
29955      * Note: the field should not have been rendered yet. For a field that has already been
29956      * rendered, use {@link #addElement}.
29957      * @param {Roo.form.Field} field
29958      * @return {Roo.ToolbarItem}
29959      */
29960      
29961       
29962     addField : function(field) {
29963         if (!this.fields) {
29964             var autoId = 0;
29965             this.fields = new Roo.util.MixedCollection(false, function(o){
29966                 return o.id || ("item" + (++autoId));
29967             });
29968
29969         }
29970         
29971         var td = this.nextBlock();
29972         field.render(td);
29973         var ti = new Roo.Toolbar.Item(td.firstChild);
29974         ti.render(td);
29975         this.items.add(ti);
29976         this.fields.add(field);
29977         return ti;
29978     },
29979     /**
29980      * Hide the toolbar
29981      * @method hide
29982      */
29983      
29984       
29985     hide : function()
29986     {
29987         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
29988         this.el.child('div').hide();
29989     },
29990     /**
29991      * Show the toolbar
29992      * @method show
29993      */
29994     show : function()
29995     {
29996         this.el.child('div').show();
29997     },
29998       
29999     // private
30000     nextBlock : function(){
30001         var td = document.createElement("td");
30002         this.tr.appendChild(td);
30003         return td;
30004     },
30005
30006     // private
30007     destroy : function(){
30008         if(this.items){ // rendered?
30009             Roo.destroy.apply(Roo, this.items.items);
30010         }
30011         if(this.fields){ // rendered?
30012             Roo.destroy.apply(Roo, this.fields.items);
30013         }
30014         Roo.Element.uncache(this.el, this.tr);
30015     }
30016 };
30017
30018 /**
30019  * @class Roo.Toolbar.Item
30020  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30021  * @constructor
30022  * Creates a new Item
30023  * @param {HTMLElement} el 
30024  */
30025 Roo.Toolbar.Item = function(el){
30026     var cfg = {};
30027     if (typeof (el.xtype) != 'undefined') {
30028         cfg = el;
30029         el = cfg.el;
30030     }
30031     
30032     this.el = Roo.getDom(el);
30033     this.id = Roo.id(this.el);
30034     this.hidden = false;
30035     
30036     this.addEvents({
30037          /**
30038              * @event render
30039              * Fires when the button is rendered
30040              * @param {Button} this
30041              */
30042         'render': true
30043     });
30044     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30045 };
30046 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30047 //Roo.Toolbar.Item.prototype = {
30048     
30049     /**
30050      * Get this item's HTML Element
30051      * @return {HTMLElement}
30052      */
30053     getEl : function(){
30054        return this.el;  
30055     },
30056
30057     // private
30058     render : function(td){
30059         
30060          this.td = td;
30061         td.appendChild(this.el);
30062         
30063         this.fireEvent('render', this);
30064     },
30065     
30066     /**
30067      * Removes and destroys this item.
30068      */
30069     destroy : function(){
30070         this.td.parentNode.removeChild(this.td);
30071     },
30072     
30073     /**
30074      * Shows this item.
30075      */
30076     show: function(){
30077         this.hidden = false;
30078         this.td.style.display = "";
30079     },
30080     
30081     /**
30082      * Hides this item.
30083      */
30084     hide: function(){
30085         this.hidden = true;
30086         this.td.style.display = "none";
30087     },
30088     
30089     /**
30090      * Convenience function for boolean show/hide.
30091      * @param {Boolean} visible true to show/false to hide
30092      */
30093     setVisible: function(visible){
30094         if(visible) {
30095             this.show();
30096         }else{
30097             this.hide();
30098         }
30099     },
30100     
30101     /**
30102      * Try to focus this item.
30103      */
30104     focus : function(){
30105         Roo.fly(this.el).focus();
30106     },
30107     
30108     /**
30109      * Disables this item.
30110      */
30111     disable : function(){
30112         Roo.fly(this.td).addClass("x-item-disabled");
30113         this.disabled = true;
30114         this.el.disabled = true;
30115     },
30116     
30117     /**
30118      * Enables this item.
30119      */
30120     enable : function(){
30121         Roo.fly(this.td).removeClass("x-item-disabled");
30122         this.disabled = false;
30123         this.el.disabled = false;
30124     }
30125 });
30126
30127
30128 /**
30129  * @class Roo.Toolbar.Separator
30130  * @extends Roo.Toolbar.Item
30131  * A simple toolbar separator class
30132  * @constructor
30133  * Creates a new Separator
30134  */
30135 Roo.Toolbar.Separator = function(cfg){
30136     
30137     var s = document.createElement("span");
30138     s.className = "ytb-sep";
30139     if (cfg) {
30140         cfg.el = s;
30141     }
30142     
30143     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30144 };
30145 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30146     enable:Roo.emptyFn,
30147     disable:Roo.emptyFn,
30148     focus:Roo.emptyFn
30149 });
30150
30151 /**
30152  * @class Roo.Toolbar.Spacer
30153  * @extends Roo.Toolbar.Item
30154  * A simple element that adds extra horizontal space to a toolbar.
30155  * @constructor
30156  * Creates a new Spacer
30157  */
30158 Roo.Toolbar.Spacer = function(cfg){
30159     var s = document.createElement("div");
30160     s.className = "ytb-spacer";
30161     if (cfg) {
30162         cfg.el = s;
30163     }
30164     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30165 };
30166 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30167     enable:Roo.emptyFn,
30168     disable:Roo.emptyFn,
30169     focus:Roo.emptyFn
30170 });
30171
30172 /**
30173  * @class Roo.Toolbar.Fill
30174  * @extends Roo.Toolbar.Spacer
30175  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30176  * @constructor
30177  * Creates a new Spacer
30178  */
30179 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30180     // private
30181     render : function(td){
30182         td.style.width = '100%';
30183         Roo.Toolbar.Fill.superclass.render.call(this, td);
30184     }
30185 });
30186
30187 /**
30188  * @class Roo.Toolbar.TextItem
30189  * @extends Roo.Toolbar.Item
30190  * A simple class that renders text directly into a toolbar.
30191  * @constructor
30192  * Creates a new TextItem
30193  * @param {String} text
30194  */
30195 Roo.Toolbar.TextItem = function(cfg){
30196     var  text = cfg || "";
30197     if (typeof(cfg) == 'object') {
30198         text = cfg.text || "";
30199     }  else {
30200         cfg = null;
30201     }
30202     var s = document.createElement("span");
30203     s.className = "ytb-text";
30204     s.innerHTML = text;
30205     if (cfg) {
30206         cfg.el  = s;
30207     }
30208     
30209     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30210 };
30211 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30212     
30213      
30214     enable:Roo.emptyFn,
30215     disable:Roo.emptyFn,
30216     focus:Roo.emptyFn
30217 });
30218
30219 /**
30220  * @class Roo.Toolbar.Button
30221  * @extends Roo.Button
30222  * A button that renders into a toolbar.
30223  * @constructor
30224  * Creates a new Button
30225  * @param {Object} config A standard {@link Roo.Button} config object
30226  */
30227 Roo.Toolbar.Button = function(config){
30228     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30229 };
30230 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30231     render : function(td){
30232         this.td = td;
30233         Roo.Toolbar.Button.superclass.render.call(this, td);
30234     },
30235     
30236     /**
30237      * Removes and destroys this button
30238      */
30239     destroy : function(){
30240         Roo.Toolbar.Button.superclass.destroy.call(this);
30241         this.td.parentNode.removeChild(this.td);
30242     },
30243     
30244     /**
30245      * Shows this button
30246      */
30247     show: function(){
30248         this.hidden = false;
30249         this.td.style.display = "";
30250     },
30251     
30252     /**
30253      * Hides this button
30254      */
30255     hide: function(){
30256         this.hidden = true;
30257         this.td.style.display = "none";
30258     },
30259
30260     /**
30261      * Disables this item
30262      */
30263     disable : function(){
30264         Roo.fly(this.td).addClass("x-item-disabled");
30265         this.disabled = true;
30266     },
30267
30268     /**
30269      * Enables this item
30270      */
30271     enable : function(){
30272         Roo.fly(this.td).removeClass("x-item-disabled");
30273         this.disabled = false;
30274     }
30275 });
30276 // backwards compat
30277 Roo.ToolbarButton = Roo.Toolbar.Button;
30278
30279 /**
30280  * @class Roo.Toolbar.SplitButton
30281  * @extends Roo.SplitButton
30282  * A menu button that renders into a toolbar.
30283  * @constructor
30284  * Creates a new SplitButton
30285  * @param {Object} config A standard {@link Roo.SplitButton} config object
30286  */
30287 Roo.Toolbar.SplitButton = function(config){
30288     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30289 };
30290 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30291     render : function(td){
30292         this.td = td;
30293         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30294     },
30295     
30296     /**
30297      * Removes and destroys this button
30298      */
30299     destroy : function(){
30300         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30301         this.td.parentNode.removeChild(this.td);
30302     },
30303     
30304     /**
30305      * Shows this button
30306      */
30307     show: function(){
30308         this.hidden = false;
30309         this.td.style.display = "";
30310     },
30311     
30312     /**
30313      * Hides this button
30314      */
30315     hide: function(){
30316         this.hidden = true;
30317         this.td.style.display = "none";
30318     }
30319 });
30320
30321 // backwards compat
30322 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30323  * Based on:
30324  * Ext JS Library 1.1.1
30325  * Copyright(c) 2006-2007, Ext JS, LLC.
30326  *
30327  * Originally Released Under LGPL - original licence link has changed is not relivant.
30328  *
30329  * Fork - LGPL
30330  * <script type="text/javascript">
30331  */
30332  
30333 /**
30334  * @class Roo.PagingToolbar
30335  * @extends Roo.Toolbar
30336  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30337  * @constructor
30338  * Create a new PagingToolbar
30339  * @param {Object} config The config object
30340  */
30341 Roo.PagingToolbar = function(el, ds, config)
30342 {
30343     // old args format still supported... - xtype is prefered..
30344     if (typeof(el) == 'object' && el.xtype) {
30345         // created from xtype...
30346         config = el;
30347         ds = el.dataSource;
30348         el = config.container;
30349     }
30350     var items = [];
30351     if (config.items) {
30352         items = config.items;
30353         config.items = [];
30354     }
30355     
30356     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30357     this.ds = ds;
30358     this.cursor = 0;
30359     this.renderButtons(this.el);
30360     this.bind(ds);
30361     
30362     // supprot items array.
30363    
30364     Roo.each(items, function(e) {
30365         this.add(Roo.factory(e));
30366     },this);
30367     
30368 };
30369
30370 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30371     /**
30372      * @cfg {Roo.data.Store} dataSource
30373      * The underlying data store providing the paged data
30374      */
30375     /**
30376      * @cfg {String/HTMLElement/Element} container
30377      * container The id or element that will contain the toolbar
30378      */
30379     /**
30380      * @cfg {Boolean} displayInfo
30381      * True to display the displayMsg (defaults to false)
30382      */
30383     /**
30384      * @cfg {Number} pageSize
30385      * The number of records to display per page (defaults to 20)
30386      */
30387     pageSize: 20,
30388     /**
30389      * @cfg {String} displayMsg
30390      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30391      */
30392     displayMsg : 'Displaying {0} - {1} of {2}',
30393     /**
30394      * @cfg {String} emptyMsg
30395      * The message to display when no records are found (defaults to "No data to display")
30396      */
30397     emptyMsg : 'No data to display',
30398     /**
30399      * Customizable piece of the default paging text (defaults to "Page")
30400      * @type String
30401      */
30402     beforePageText : "Page",
30403     /**
30404      * Customizable piece of the default paging text (defaults to "of %0")
30405      * @type String
30406      */
30407     afterPageText : "of {0}",
30408     /**
30409      * Customizable piece of the default paging text (defaults to "First Page")
30410      * @type String
30411      */
30412     firstText : "First Page",
30413     /**
30414      * Customizable piece of the default paging text (defaults to "Previous Page")
30415      * @type String
30416      */
30417     prevText : "Previous Page",
30418     /**
30419      * Customizable piece of the default paging text (defaults to "Next Page")
30420      * @type String
30421      */
30422     nextText : "Next Page",
30423     /**
30424      * Customizable piece of the default paging text (defaults to "Last Page")
30425      * @type String
30426      */
30427     lastText : "Last Page",
30428     /**
30429      * Customizable piece of the default paging text (defaults to "Refresh")
30430      * @type String
30431      */
30432     refreshText : "Refresh",
30433
30434     // private
30435     renderButtons : function(el){
30436         Roo.PagingToolbar.superclass.render.call(this, el);
30437         this.first = this.addButton({
30438             tooltip: this.firstText,
30439             cls: "x-btn-icon x-grid-page-first",
30440             disabled: true,
30441             handler: this.onClick.createDelegate(this, ["first"])
30442         });
30443         this.prev = this.addButton({
30444             tooltip: this.prevText,
30445             cls: "x-btn-icon x-grid-page-prev",
30446             disabled: true,
30447             handler: this.onClick.createDelegate(this, ["prev"])
30448         });
30449         //this.addSeparator();
30450         this.add(this.beforePageText);
30451         this.field = Roo.get(this.addDom({
30452            tag: "input",
30453            type: "text",
30454            size: "3",
30455            value: "1",
30456            cls: "x-grid-page-number"
30457         }).el);
30458         this.field.on("keydown", this.onPagingKeydown, this);
30459         this.field.on("focus", function(){this.dom.select();});
30460         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30461         this.field.setHeight(18);
30462         //this.addSeparator();
30463         this.next = this.addButton({
30464             tooltip: this.nextText,
30465             cls: "x-btn-icon x-grid-page-next",
30466             disabled: true,
30467             handler: this.onClick.createDelegate(this, ["next"])
30468         });
30469         this.last = this.addButton({
30470             tooltip: this.lastText,
30471             cls: "x-btn-icon x-grid-page-last",
30472             disabled: true,
30473             handler: this.onClick.createDelegate(this, ["last"])
30474         });
30475         //this.addSeparator();
30476         this.loading = this.addButton({
30477             tooltip: this.refreshText,
30478             cls: "x-btn-icon x-grid-loading",
30479             handler: this.onClick.createDelegate(this, ["refresh"])
30480         });
30481
30482         if(this.displayInfo){
30483             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30484         }
30485     },
30486
30487     // private
30488     updateInfo : function(){
30489         if(this.displayEl){
30490             var count = this.ds.getCount();
30491             var msg = count == 0 ?
30492                 this.emptyMsg :
30493                 String.format(
30494                     this.displayMsg,
30495                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30496                 );
30497             this.displayEl.update(msg);
30498         }
30499     },
30500
30501     // private
30502     onLoad : function(ds, r, o){
30503        this.cursor = o.params ? o.params.start : 0;
30504        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30505
30506        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30507        this.field.dom.value = ap;
30508        this.first.setDisabled(ap == 1);
30509        this.prev.setDisabled(ap == 1);
30510        this.next.setDisabled(ap == ps);
30511        this.last.setDisabled(ap == ps);
30512        this.loading.enable();
30513        this.updateInfo();
30514     },
30515
30516     // private
30517     getPageData : function(){
30518         var total = this.ds.getTotalCount();
30519         return {
30520             total : total,
30521             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30522             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30523         };
30524     },
30525
30526     // private
30527     onLoadError : function(){
30528         this.loading.enable();
30529     },
30530
30531     // private
30532     onPagingKeydown : function(e){
30533         var k = e.getKey();
30534         var d = this.getPageData();
30535         if(k == e.RETURN){
30536             var v = this.field.dom.value, pageNum;
30537             if(!v || isNaN(pageNum = parseInt(v, 10))){
30538                 this.field.dom.value = d.activePage;
30539                 return;
30540             }
30541             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30542             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30543             e.stopEvent();
30544         }
30545         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))
30546         {
30547           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30548           this.field.dom.value = pageNum;
30549           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30550           e.stopEvent();
30551         }
30552         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30553         {
30554           var v = this.field.dom.value, pageNum; 
30555           var increment = (e.shiftKey) ? 10 : 1;
30556           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30557             increment *= -1;
30558           }
30559           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30560             this.field.dom.value = d.activePage;
30561             return;
30562           }
30563           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30564           {
30565             this.field.dom.value = parseInt(v, 10) + increment;
30566             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30567             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30568           }
30569           e.stopEvent();
30570         }
30571     },
30572
30573     // private
30574     beforeLoad : function(){
30575         if(this.loading){
30576             this.loading.disable();
30577         }
30578     },
30579
30580     // private
30581     onClick : function(which){
30582         var ds = this.ds;
30583         switch(which){
30584             case "first":
30585                 ds.load({params:{start: 0, limit: this.pageSize}});
30586             break;
30587             case "prev":
30588                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30589             break;
30590             case "next":
30591                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30592             break;
30593             case "last":
30594                 var total = ds.getTotalCount();
30595                 var extra = total % this.pageSize;
30596                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30597                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30598             break;
30599             case "refresh":
30600                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30601             break;
30602         }
30603     },
30604
30605     /**
30606      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30607      * @param {Roo.data.Store} store The data store to unbind
30608      */
30609     unbind : function(ds){
30610         ds.un("beforeload", this.beforeLoad, this);
30611         ds.un("load", this.onLoad, this);
30612         ds.un("loadexception", this.onLoadError, this);
30613         ds.un("remove", this.updateInfo, this);
30614         ds.un("add", this.updateInfo, this);
30615         this.ds = undefined;
30616     },
30617
30618     /**
30619      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30620      * @param {Roo.data.Store} store The data store to bind
30621      */
30622     bind : function(ds){
30623         ds.on("beforeload", this.beforeLoad, this);
30624         ds.on("load", this.onLoad, this);
30625         ds.on("loadexception", this.onLoadError, this);
30626         ds.on("remove", this.updateInfo, this);
30627         ds.on("add", this.updateInfo, this);
30628         this.ds = ds;
30629     }
30630 });/*
30631  * Based on:
30632  * Ext JS Library 1.1.1
30633  * Copyright(c) 2006-2007, Ext JS, LLC.
30634  *
30635  * Originally Released Under LGPL - original licence link has changed is not relivant.
30636  *
30637  * Fork - LGPL
30638  * <script type="text/javascript">
30639  */
30640
30641 /**
30642  * @class Roo.Resizable
30643  * @extends Roo.util.Observable
30644  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30645  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30646  * 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
30647  * the element will be wrapped for you automatically.</p>
30648  * <p>Here is the list of valid resize handles:</p>
30649  * <pre>
30650 Value   Description
30651 ------  -------------------
30652  'n'     north
30653  's'     south
30654  'e'     east
30655  'w'     west
30656  'nw'    northwest
30657  'sw'    southwest
30658  'se'    southeast
30659  'ne'    northeast
30660  'hd'    horizontal drag
30661  'all'   all
30662 </pre>
30663  * <p>Here's an example showing the creation of a typical Resizable:</p>
30664  * <pre><code>
30665 var resizer = new Roo.Resizable("element-id", {
30666     handles: 'all',
30667     minWidth: 200,
30668     minHeight: 100,
30669     maxWidth: 500,
30670     maxHeight: 400,
30671     pinned: true
30672 });
30673 resizer.on("resize", myHandler);
30674 </code></pre>
30675  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30676  * resizer.east.setDisplayed(false);</p>
30677  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30678  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30679  * resize operation's new size (defaults to [0, 0])
30680  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30681  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30682  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30683  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30684  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30685  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30686  * @cfg {Number} width The width of the element in pixels (defaults to null)
30687  * @cfg {Number} height The height of the element in pixels (defaults to null)
30688  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30689  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30690  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30691  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30692  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30693  * in favor of the handles config option (defaults to false)
30694  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30695  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30696  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30697  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30698  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30699  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30700  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30701  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30702  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30703  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30704  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30705  * @constructor
30706  * Create a new resizable component
30707  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30708  * @param {Object} config configuration options
30709   */
30710 Roo.Resizable = function(el, config)
30711 {
30712     this.el = Roo.get(el);
30713
30714     if(config && config.wrap){
30715         config.resizeChild = this.el;
30716         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30717         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30718         this.el.setStyle("overflow", "hidden");
30719         this.el.setPositioning(config.resizeChild.getPositioning());
30720         config.resizeChild.clearPositioning();
30721         if(!config.width || !config.height){
30722             var csize = config.resizeChild.getSize();
30723             this.el.setSize(csize.width, csize.height);
30724         }
30725         if(config.pinned && !config.adjustments){
30726             config.adjustments = "auto";
30727         }
30728     }
30729
30730     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30731     this.proxy.unselectable();
30732     this.proxy.enableDisplayMode('block');
30733
30734     Roo.apply(this, config);
30735
30736     if(this.pinned){
30737         this.disableTrackOver = true;
30738         this.el.addClass("x-resizable-pinned");
30739     }
30740     // if the element isn't positioned, make it relative
30741     var position = this.el.getStyle("position");
30742     if(position != "absolute" && position != "fixed"){
30743         this.el.setStyle("position", "relative");
30744     }
30745     if(!this.handles){ // no handles passed, must be legacy style
30746         this.handles = 's,e,se';
30747         if(this.multiDirectional){
30748             this.handles += ',n,w';
30749         }
30750     }
30751     if(this.handles == "all"){
30752         this.handles = "n s e w ne nw se sw";
30753     }
30754     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30755     var ps = Roo.Resizable.positions;
30756     for(var i = 0, len = hs.length; i < len; i++){
30757         if(hs[i] && ps[hs[i]]){
30758             var pos = ps[hs[i]];
30759             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30760         }
30761     }
30762     // legacy
30763     this.corner = this.southeast;
30764     
30765     // updateBox = the box can move..
30766     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30767         this.updateBox = true;
30768     }
30769
30770     this.activeHandle = null;
30771
30772     if(this.resizeChild){
30773         if(typeof this.resizeChild == "boolean"){
30774             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30775         }else{
30776             this.resizeChild = Roo.get(this.resizeChild, true);
30777         }
30778     }
30779     
30780     if(this.adjustments == "auto"){
30781         var rc = this.resizeChild;
30782         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30783         if(rc && (hw || hn)){
30784             rc.position("relative");
30785             rc.setLeft(hw ? hw.el.getWidth() : 0);
30786             rc.setTop(hn ? hn.el.getHeight() : 0);
30787         }
30788         this.adjustments = [
30789             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30790             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30791         ];
30792     }
30793
30794     if(this.draggable){
30795         this.dd = this.dynamic ?
30796             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30797         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30798     }
30799
30800     // public events
30801     this.addEvents({
30802         /**
30803          * @event beforeresize
30804          * Fired before resize is allowed. Set enabled to false to cancel resize.
30805          * @param {Roo.Resizable} this
30806          * @param {Roo.EventObject} e The mousedown event
30807          */
30808         "beforeresize" : true,
30809         /**
30810          * @event resizing
30811          * Fired a resizing.
30812          * @param {Roo.Resizable} this
30813          * @param {Number} x The new x position
30814          * @param {Number} y The new y position
30815          * @param {Number} w The new w width
30816          * @param {Number} h The new h hight
30817          * @param {Roo.EventObject} e The mouseup event
30818          */
30819         "resizing" : true,
30820         /**
30821          * @event resize
30822          * Fired after a resize.
30823          * @param {Roo.Resizable} this
30824          * @param {Number} width The new width
30825          * @param {Number} height The new height
30826          * @param {Roo.EventObject} e The mouseup event
30827          */
30828         "resize" : true
30829     });
30830
30831     if(this.width !== null && this.height !== null){
30832         this.resizeTo(this.width, this.height);
30833     }else{
30834         this.updateChildSize();
30835     }
30836     if(Roo.isIE){
30837         this.el.dom.style.zoom = 1;
30838     }
30839     Roo.Resizable.superclass.constructor.call(this);
30840 };
30841
30842 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30843         resizeChild : false,
30844         adjustments : [0, 0],
30845         minWidth : 5,
30846         minHeight : 5,
30847         maxWidth : 10000,
30848         maxHeight : 10000,
30849         enabled : true,
30850         animate : false,
30851         duration : .35,
30852         dynamic : false,
30853         handles : false,
30854         multiDirectional : false,
30855         disableTrackOver : false,
30856         easing : 'easeOutStrong',
30857         widthIncrement : 0,
30858         heightIncrement : 0,
30859         pinned : false,
30860         width : null,
30861         height : null,
30862         preserveRatio : false,
30863         transparent: false,
30864         minX: 0,
30865         minY: 0,
30866         draggable: false,
30867
30868         /**
30869          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30870          */
30871         constrainTo: undefined,
30872         /**
30873          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30874          */
30875         resizeRegion: undefined,
30876
30877
30878     /**
30879      * Perform a manual resize
30880      * @param {Number} width
30881      * @param {Number} height
30882      */
30883     resizeTo : function(width, height){
30884         this.el.setSize(width, height);
30885         this.updateChildSize();
30886         this.fireEvent("resize", this, width, height, null);
30887     },
30888
30889     // private
30890     startSizing : function(e, handle){
30891         this.fireEvent("beforeresize", this, e);
30892         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30893
30894             if(!this.overlay){
30895                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30896                 this.overlay.unselectable();
30897                 this.overlay.enableDisplayMode("block");
30898                 this.overlay.on("mousemove", this.onMouseMove, this);
30899                 this.overlay.on("mouseup", this.onMouseUp, this);
30900             }
30901             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30902
30903             this.resizing = true;
30904             this.startBox = this.el.getBox();
30905             this.startPoint = e.getXY();
30906             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30907                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30908
30909             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30910             this.overlay.show();
30911
30912             if(this.constrainTo) {
30913                 var ct = Roo.get(this.constrainTo);
30914                 this.resizeRegion = ct.getRegion().adjust(
30915                     ct.getFrameWidth('t'),
30916                     ct.getFrameWidth('l'),
30917                     -ct.getFrameWidth('b'),
30918                     -ct.getFrameWidth('r')
30919                 );
30920             }
30921
30922             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30923             this.proxy.show();
30924             this.proxy.setBox(this.startBox);
30925             if(!this.dynamic){
30926                 this.proxy.setStyle('visibility', 'visible');
30927             }
30928         }
30929     },
30930
30931     // private
30932     onMouseDown : function(handle, e){
30933         if(this.enabled){
30934             e.stopEvent();
30935             this.activeHandle = handle;
30936             this.startSizing(e, handle);
30937         }
30938     },
30939
30940     // private
30941     onMouseUp : function(e){
30942         var size = this.resizeElement();
30943         this.resizing = false;
30944         this.handleOut();
30945         this.overlay.hide();
30946         this.proxy.hide();
30947         this.fireEvent("resize", this, size.width, size.height, e);
30948     },
30949
30950     // private
30951     updateChildSize : function(){
30952         
30953         if(this.resizeChild){
30954             var el = this.el;
30955             var child = this.resizeChild;
30956             var adj = this.adjustments;
30957             if(el.dom.offsetWidth){
30958                 var b = el.getSize(true);
30959                 child.setSize(b.width+adj[0], b.height+adj[1]);
30960             }
30961             // Second call here for IE
30962             // The first call enables instant resizing and
30963             // the second call corrects scroll bars if they
30964             // exist
30965             if(Roo.isIE){
30966                 setTimeout(function(){
30967                     if(el.dom.offsetWidth){
30968                         var b = el.getSize(true);
30969                         child.setSize(b.width+adj[0], b.height+adj[1]);
30970                     }
30971                 }, 10);
30972             }
30973         }
30974     },
30975
30976     // private
30977     snap : function(value, inc, min){
30978         if(!inc || !value) {
30979             return value;
30980         }
30981         var newValue = value;
30982         var m = value % inc;
30983         if(m > 0){
30984             if(m > (inc/2)){
30985                 newValue = value + (inc-m);
30986             }else{
30987                 newValue = value - m;
30988             }
30989         }
30990         return Math.max(min, newValue);
30991     },
30992
30993     // private
30994     resizeElement : function(){
30995         var box = this.proxy.getBox();
30996         if(this.updateBox){
30997             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
30998         }else{
30999             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31000         }
31001         this.updateChildSize();
31002         if(!this.dynamic){
31003             this.proxy.hide();
31004         }
31005         return box;
31006     },
31007
31008     // private
31009     constrain : function(v, diff, m, mx){
31010         if(v - diff < m){
31011             diff = v - m;
31012         }else if(v - diff > mx){
31013             diff = mx - v;
31014         }
31015         return diff;
31016     },
31017
31018     // private
31019     onMouseMove : function(e){
31020         
31021         if(this.enabled){
31022             try{// try catch so if something goes wrong the user doesn't get hung
31023
31024             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31025                 return;
31026             }
31027
31028             //var curXY = this.startPoint;
31029             var curSize = this.curSize || this.startBox;
31030             var x = this.startBox.x, y = this.startBox.y;
31031             var ox = x, oy = y;
31032             var w = curSize.width, h = curSize.height;
31033             var ow = w, oh = h;
31034             var mw = this.minWidth, mh = this.minHeight;
31035             var mxw = this.maxWidth, mxh = this.maxHeight;
31036             var wi = this.widthIncrement;
31037             var hi = this.heightIncrement;
31038
31039             var eventXY = e.getXY();
31040             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31041             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31042
31043             var pos = this.activeHandle.position;
31044
31045             switch(pos){
31046                 case "east":
31047                     w += diffX;
31048                     w = Math.min(Math.max(mw, w), mxw);
31049                     break;
31050              
31051                 case "south":
31052                     h += diffY;
31053                     h = Math.min(Math.max(mh, h), mxh);
31054                     break;
31055                 case "southeast":
31056                     w += diffX;
31057                     h += diffY;
31058                     w = Math.min(Math.max(mw, w), mxw);
31059                     h = Math.min(Math.max(mh, h), mxh);
31060                     break;
31061                 case "north":
31062                     diffY = this.constrain(h, diffY, mh, mxh);
31063                     y += diffY;
31064                     h -= diffY;
31065                     break;
31066                 case "hdrag":
31067                     
31068                     if (wi) {
31069                         var adiffX = Math.abs(diffX);
31070                         var sub = (adiffX % wi); // how much 
31071                         if (sub > (wi/2)) { // far enough to snap
31072                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31073                         } else {
31074                             // remove difference.. 
31075                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31076                         }
31077                     }
31078                     x += diffX;
31079                     x = Math.max(this.minX, x);
31080                     break;
31081                 case "west":
31082                     diffX = this.constrain(w, diffX, mw, mxw);
31083                     x += diffX;
31084                     w -= diffX;
31085                     break;
31086                 case "northeast":
31087                     w += diffX;
31088                     w = Math.min(Math.max(mw, w), mxw);
31089                     diffY = this.constrain(h, diffY, mh, mxh);
31090                     y += diffY;
31091                     h -= diffY;
31092                     break;
31093                 case "northwest":
31094                     diffX = this.constrain(w, diffX, mw, mxw);
31095                     diffY = this.constrain(h, diffY, mh, mxh);
31096                     y += diffY;
31097                     h -= diffY;
31098                     x += diffX;
31099                     w -= diffX;
31100                     break;
31101                case "southwest":
31102                     diffX = this.constrain(w, diffX, mw, mxw);
31103                     h += diffY;
31104                     h = Math.min(Math.max(mh, h), mxh);
31105                     x += diffX;
31106                     w -= diffX;
31107                     break;
31108             }
31109
31110             var sw = this.snap(w, wi, mw);
31111             var sh = this.snap(h, hi, mh);
31112             if(sw != w || sh != h){
31113                 switch(pos){
31114                     case "northeast":
31115                         y -= sh - h;
31116                     break;
31117                     case "north":
31118                         y -= sh - h;
31119                         break;
31120                     case "southwest":
31121                         x -= sw - w;
31122                     break;
31123                     case "west":
31124                         x -= sw - w;
31125                         break;
31126                     case "northwest":
31127                         x -= sw - w;
31128                         y -= sh - h;
31129                     break;
31130                 }
31131                 w = sw;
31132                 h = sh;
31133             }
31134
31135             if(this.preserveRatio){
31136                 switch(pos){
31137                     case "southeast":
31138                     case "east":
31139                         h = oh * (w/ow);
31140                         h = Math.min(Math.max(mh, h), mxh);
31141                         w = ow * (h/oh);
31142                        break;
31143                     case "south":
31144                         w = ow * (h/oh);
31145                         w = Math.min(Math.max(mw, w), mxw);
31146                         h = oh * (w/ow);
31147                         break;
31148                     case "northeast":
31149                         w = ow * (h/oh);
31150                         w = Math.min(Math.max(mw, w), mxw);
31151                         h = oh * (w/ow);
31152                     break;
31153                     case "north":
31154                         var tw = w;
31155                         w = ow * (h/oh);
31156                         w = Math.min(Math.max(mw, w), mxw);
31157                         h = oh * (w/ow);
31158                         x += (tw - w) / 2;
31159                         break;
31160                     case "southwest":
31161                         h = oh * (w/ow);
31162                         h = Math.min(Math.max(mh, h), mxh);
31163                         var tw = w;
31164                         w = ow * (h/oh);
31165                         x += tw - w;
31166                         break;
31167                     case "west":
31168                         var th = h;
31169                         h = oh * (w/ow);
31170                         h = Math.min(Math.max(mh, h), mxh);
31171                         y += (th - h) / 2;
31172                         var tw = w;
31173                         w = ow * (h/oh);
31174                         x += tw - w;
31175                        break;
31176                     case "northwest":
31177                         var tw = w;
31178                         var th = h;
31179                         h = oh * (w/ow);
31180                         h = Math.min(Math.max(mh, h), mxh);
31181                         w = ow * (h/oh);
31182                         y += th - h;
31183                         x += tw - w;
31184                        break;
31185
31186                 }
31187             }
31188             if (pos == 'hdrag') {
31189                 w = ow;
31190             }
31191             this.proxy.setBounds(x, y, w, h);
31192             if(this.dynamic){
31193                 this.resizeElement();
31194             }
31195             }catch(e){}
31196         }
31197         this.fireEvent("resizing", this, x, y, w, h, e);
31198     },
31199
31200     // private
31201     handleOver : function(){
31202         if(this.enabled){
31203             this.el.addClass("x-resizable-over");
31204         }
31205     },
31206
31207     // private
31208     handleOut : function(){
31209         if(!this.resizing){
31210             this.el.removeClass("x-resizable-over");
31211         }
31212     },
31213
31214     /**
31215      * Returns the element this component is bound to.
31216      * @return {Roo.Element}
31217      */
31218     getEl : function(){
31219         return this.el;
31220     },
31221
31222     /**
31223      * Returns the resizeChild element (or null).
31224      * @return {Roo.Element}
31225      */
31226     getResizeChild : function(){
31227         return this.resizeChild;
31228     },
31229     groupHandler : function()
31230     {
31231         
31232     },
31233     /**
31234      * Destroys this resizable. If the element was wrapped and
31235      * removeEl is not true then the element remains.
31236      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31237      */
31238     destroy : function(removeEl){
31239         this.proxy.remove();
31240         if(this.overlay){
31241             this.overlay.removeAllListeners();
31242             this.overlay.remove();
31243         }
31244         var ps = Roo.Resizable.positions;
31245         for(var k in ps){
31246             if(typeof ps[k] != "function" && this[ps[k]]){
31247                 var h = this[ps[k]];
31248                 h.el.removeAllListeners();
31249                 h.el.remove();
31250             }
31251         }
31252         if(removeEl){
31253             this.el.update("");
31254             this.el.remove();
31255         }
31256     }
31257 });
31258
31259 // private
31260 // hash to map config positions to true positions
31261 Roo.Resizable.positions = {
31262     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31263     hd: "hdrag"
31264 };
31265
31266 // private
31267 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31268     if(!this.tpl){
31269         // only initialize the template if resizable is used
31270         var tpl = Roo.DomHelper.createTemplate(
31271             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31272         );
31273         tpl.compile();
31274         Roo.Resizable.Handle.prototype.tpl = tpl;
31275     }
31276     this.position = pos;
31277     this.rz = rz;
31278     // show north drag fro topdra
31279     var handlepos = pos == 'hdrag' ? 'north' : pos;
31280     
31281     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31282     if (pos == 'hdrag') {
31283         this.el.setStyle('cursor', 'pointer');
31284     }
31285     this.el.unselectable();
31286     if(transparent){
31287         this.el.setOpacity(0);
31288     }
31289     this.el.on("mousedown", this.onMouseDown, this);
31290     if(!disableTrackOver){
31291         this.el.on("mouseover", this.onMouseOver, this);
31292         this.el.on("mouseout", this.onMouseOut, this);
31293     }
31294 };
31295
31296 // private
31297 Roo.Resizable.Handle.prototype = {
31298     afterResize : function(rz){
31299         Roo.log('after?');
31300         // do nothing
31301     },
31302     // private
31303     onMouseDown : function(e){
31304         this.rz.onMouseDown(this, e);
31305     },
31306     // private
31307     onMouseOver : function(e){
31308         this.rz.handleOver(this, e);
31309     },
31310     // private
31311     onMouseOut : function(e){
31312         this.rz.handleOut(this, e);
31313     }
31314 };/*
31315  * Based on:
31316  * Ext JS Library 1.1.1
31317  * Copyright(c) 2006-2007, Ext JS, LLC.
31318  *
31319  * Originally Released Under LGPL - original licence link has changed is not relivant.
31320  *
31321  * Fork - LGPL
31322  * <script type="text/javascript">
31323  */
31324
31325 /**
31326  * @class Roo.Editor
31327  * @extends Roo.Component
31328  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31329  * @constructor
31330  * Create a new Editor
31331  * @param {Roo.form.Field} field The Field object (or descendant)
31332  * @param {Object} config The config object
31333  */
31334 Roo.Editor = function(field, config){
31335     Roo.Editor.superclass.constructor.call(this, config);
31336     this.field = field;
31337     this.addEvents({
31338         /**
31339              * @event beforestartedit
31340              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31341              * false from the handler of this event.
31342              * @param {Editor} this
31343              * @param {Roo.Element} boundEl The underlying element bound to this editor
31344              * @param {Mixed} value The field value being set
31345              */
31346         "beforestartedit" : true,
31347         /**
31348              * @event startedit
31349              * Fires when this editor is displayed
31350              * @param {Roo.Element} boundEl The underlying element bound to this editor
31351              * @param {Mixed} value The starting field value
31352              */
31353         "startedit" : true,
31354         /**
31355              * @event beforecomplete
31356              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31357              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31358              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31359              * event will not fire since no edit actually occurred.
31360              * @param {Editor} this
31361              * @param {Mixed} value The current field value
31362              * @param {Mixed} startValue The original field value
31363              */
31364         "beforecomplete" : true,
31365         /**
31366              * @event complete
31367              * Fires after editing is complete and any changed value has been written to the underlying field.
31368              * @param {Editor} this
31369              * @param {Mixed} value The current field value
31370              * @param {Mixed} startValue The original field value
31371              */
31372         "complete" : true,
31373         /**
31374          * @event specialkey
31375          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31376          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31377          * @param {Roo.form.Field} this
31378          * @param {Roo.EventObject} e The event object
31379          */
31380         "specialkey" : true
31381     });
31382 };
31383
31384 Roo.extend(Roo.Editor, Roo.Component, {
31385     /**
31386      * @cfg {Boolean/String} autosize
31387      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31388      * or "height" to adopt the height only (defaults to false)
31389      */
31390     /**
31391      * @cfg {Boolean} revertInvalid
31392      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31393      * validation fails (defaults to true)
31394      */
31395     /**
31396      * @cfg {Boolean} ignoreNoChange
31397      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31398      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31399      * will never be ignored.
31400      */
31401     /**
31402      * @cfg {Boolean} hideEl
31403      * False to keep the bound element visible while the editor is displayed (defaults to true)
31404      */
31405     /**
31406      * @cfg {Mixed} value
31407      * The data value of the underlying field (defaults to "")
31408      */
31409     value : "",
31410     /**
31411      * @cfg {String} alignment
31412      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31413      */
31414     alignment: "c-c?",
31415     /**
31416      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31417      * for bottom-right shadow (defaults to "frame")
31418      */
31419     shadow : "frame",
31420     /**
31421      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31422      */
31423     constrain : false,
31424     /**
31425      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31426      */
31427     completeOnEnter : false,
31428     /**
31429      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31430      */
31431     cancelOnEsc : false,
31432     /**
31433      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31434      */
31435     updateEl : false,
31436
31437     // private
31438     onRender : function(ct, position){
31439         this.el = new Roo.Layer({
31440             shadow: this.shadow,
31441             cls: "x-editor",
31442             parentEl : ct,
31443             shim : this.shim,
31444             shadowOffset:4,
31445             id: this.id,
31446             constrain: this.constrain
31447         });
31448         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31449         if(this.field.msgTarget != 'title'){
31450             this.field.msgTarget = 'qtip';
31451         }
31452         this.field.render(this.el);
31453         if(Roo.isGecko){
31454             this.field.el.dom.setAttribute('autocomplete', 'off');
31455         }
31456         this.field.on("specialkey", this.onSpecialKey, this);
31457         if(this.swallowKeys){
31458             this.field.el.swallowEvent(['keydown','keypress']);
31459         }
31460         this.field.show();
31461         this.field.on("blur", this.onBlur, this);
31462         if(this.field.grow){
31463             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31464         }
31465     },
31466
31467     onSpecialKey : function(field, e)
31468     {
31469         //Roo.log('editor onSpecialKey');
31470         if(this.completeOnEnter && e.getKey() == e.ENTER){
31471             e.stopEvent();
31472             this.completeEdit();
31473             return;
31474         }
31475         // do not fire special key otherwise it might hide close the editor...
31476         if(e.getKey() == e.ENTER){    
31477             return;
31478         }
31479         if(this.cancelOnEsc && e.getKey() == e.ESC){
31480             this.cancelEdit();
31481             return;
31482         } 
31483         this.fireEvent('specialkey', field, e);
31484     
31485     },
31486
31487     /**
31488      * Starts the editing process and shows the editor.
31489      * @param {String/HTMLElement/Element} el The element to edit
31490      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31491       * to the innerHTML of el.
31492      */
31493     startEdit : function(el, value){
31494         if(this.editing){
31495             this.completeEdit();
31496         }
31497         this.boundEl = Roo.get(el);
31498         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31499         if(!this.rendered){
31500             this.render(this.parentEl || document.body);
31501         }
31502         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31503             return;
31504         }
31505         this.startValue = v;
31506         this.field.setValue(v);
31507         if(this.autoSize){
31508             var sz = this.boundEl.getSize();
31509             switch(this.autoSize){
31510                 case "width":
31511                 this.setSize(sz.width,  "");
31512                 break;
31513                 case "height":
31514                 this.setSize("",  sz.height);
31515                 break;
31516                 default:
31517                 this.setSize(sz.width,  sz.height);
31518             }
31519         }
31520         this.el.alignTo(this.boundEl, this.alignment);
31521         this.editing = true;
31522         if(Roo.QuickTips){
31523             Roo.QuickTips.disable();
31524         }
31525         this.show();
31526     },
31527
31528     /**
31529      * Sets the height and width of this editor.
31530      * @param {Number} width The new width
31531      * @param {Number} height The new height
31532      */
31533     setSize : function(w, h){
31534         this.field.setSize(w, h);
31535         if(this.el){
31536             this.el.sync();
31537         }
31538     },
31539
31540     /**
31541      * Realigns the editor to the bound field based on the current alignment config value.
31542      */
31543     realign : function(){
31544         this.el.alignTo(this.boundEl, this.alignment);
31545     },
31546
31547     /**
31548      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31549      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31550      */
31551     completeEdit : function(remainVisible){
31552         if(!this.editing){
31553             return;
31554         }
31555         var v = this.getValue();
31556         if(this.revertInvalid !== false && !this.field.isValid()){
31557             v = this.startValue;
31558             this.cancelEdit(true);
31559         }
31560         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31561             this.editing = false;
31562             this.hide();
31563             return;
31564         }
31565         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31566             this.editing = false;
31567             if(this.updateEl && this.boundEl){
31568                 this.boundEl.update(v);
31569             }
31570             if(remainVisible !== true){
31571                 this.hide();
31572             }
31573             this.fireEvent("complete", this, v, this.startValue);
31574         }
31575     },
31576
31577     // private
31578     onShow : function(){
31579         this.el.show();
31580         if(this.hideEl !== false){
31581             this.boundEl.hide();
31582         }
31583         this.field.show();
31584         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31585             this.fixIEFocus = true;
31586             this.deferredFocus.defer(50, this);
31587         }else{
31588             this.field.focus();
31589         }
31590         this.fireEvent("startedit", this.boundEl, this.startValue);
31591     },
31592
31593     deferredFocus : function(){
31594         if(this.editing){
31595             this.field.focus();
31596         }
31597     },
31598
31599     /**
31600      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31601      * reverted to the original starting value.
31602      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31603      * cancel (defaults to false)
31604      */
31605     cancelEdit : function(remainVisible){
31606         if(this.editing){
31607             this.setValue(this.startValue);
31608             if(remainVisible !== true){
31609                 this.hide();
31610             }
31611         }
31612     },
31613
31614     // private
31615     onBlur : function(){
31616         if(this.allowBlur !== true && this.editing){
31617             this.completeEdit();
31618         }
31619     },
31620
31621     // private
31622     onHide : function(){
31623         if(this.editing){
31624             this.completeEdit();
31625             return;
31626         }
31627         this.field.blur();
31628         if(this.field.collapse){
31629             this.field.collapse();
31630         }
31631         this.el.hide();
31632         if(this.hideEl !== false){
31633             this.boundEl.show();
31634         }
31635         if(Roo.QuickTips){
31636             Roo.QuickTips.enable();
31637         }
31638     },
31639
31640     /**
31641      * Sets the data value of the editor
31642      * @param {Mixed} value Any valid value supported by the underlying field
31643      */
31644     setValue : function(v){
31645         this.field.setValue(v);
31646     },
31647
31648     /**
31649      * Gets the data value of the editor
31650      * @return {Mixed} The data value
31651      */
31652     getValue : function(){
31653         return this.field.getValue();
31654     }
31655 });/*
31656  * Based on:
31657  * Ext JS Library 1.1.1
31658  * Copyright(c) 2006-2007, Ext JS, LLC.
31659  *
31660  * Originally Released Under LGPL - original licence link has changed is not relivant.
31661  *
31662  * Fork - LGPL
31663  * <script type="text/javascript">
31664  */
31665  
31666 /**
31667  * @class Roo.BasicDialog
31668  * @extends Roo.util.Observable
31669  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31670  * <pre><code>
31671 var dlg = new Roo.BasicDialog("my-dlg", {
31672     height: 200,
31673     width: 300,
31674     minHeight: 100,
31675     minWidth: 150,
31676     modal: true,
31677     proxyDrag: true,
31678     shadow: true
31679 });
31680 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31681 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31682 dlg.addButton('Cancel', dlg.hide, dlg);
31683 dlg.show();
31684 </code></pre>
31685   <b>A Dialog should always be a direct child of the body element.</b>
31686  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31687  * @cfg {String} title Default text to display in the title bar (defaults to null)
31688  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31689  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31690  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31691  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31692  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31693  * (defaults to null with no animation)
31694  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31695  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31696  * property for valid values (defaults to 'all')
31697  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31698  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31699  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31700  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31701  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31702  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31703  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31704  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31705  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31706  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31707  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31708  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31709  * draggable = true (defaults to false)
31710  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31711  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31712  * shadow (defaults to false)
31713  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31714  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31715  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31716  * @cfg {Array} buttons Array of buttons
31717  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31718  * @constructor
31719  * Create a new BasicDialog.
31720  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31721  * @param {Object} config Configuration options
31722  */
31723 Roo.BasicDialog = function(el, config){
31724     this.el = Roo.get(el);
31725     var dh = Roo.DomHelper;
31726     if(!this.el && config && config.autoCreate){
31727         if(typeof config.autoCreate == "object"){
31728             if(!config.autoCreate.id){
31729                 config.autoCreate.id = el;
31730             }
31731             this.el = dh.append(document.body,
31732                         config.autoCreate, true);
31733         }else{
31734             this.el = dh.append(document.body,
31735                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31736         }
31737     }
31738     el = this.el;
31739     el.setDisplayed(true);
31740     el.hide = this.hideAction;
31741     this.id = el.id;
31742     el.addClass("x-dlg");
31743
31744     Roo.apply(this, config);
31745
31746     this.proxy = el.createProxy("x-dlg-proxy");
31747     this.proxy.hide = this.hideAction;
31748     this.proxy.setOpacity(.5);
31749     this.proxy.hide();
31750
31751     if(config.width){
31752         el.setWidth(config.width);
31753     }
31754     if(config.height){
31755         el.setHeight(config.height);
31756     }
31757     this.size = el.getSize();
31758     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31759         this.xy = [config.x,config.y];
31760     }else{
31761         this.xy = el.getCenterXY(true);
31762     }
31763     /** The header element @type Roo.Element */
31764     this.header = el.child("> .x-dlg-hd");
31765     /** The body element @type Roo.Element */
31766     this.body = el.child("> .x-dlg-bd");
31767     /** The footer element @type Roo.Element */
31768     this.footer = el.child("> .x-dlg-ft");
31769
31770     if(!this.header){
31771         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31772     }
31773     if(!this.body){
31774         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31775     }
31776
31777     this.header.unselectable();
31778     if(this.title){
31779         this.header.update(this.title);
31780     }
31781     // this element allows the dialog to be focused for keyboard event
31782     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31783     this.focusEl.swallowEvent("click", true);
31784
31785     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31786
31787     // wrap the body and footer for special rendering
31788     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31789     if(this.footer){
31790         this.bwrap.dom.appendChild(this.footer.dom);
31791     }
31792
31793     this.bg = this.el.createChild({
31794         tag: "div", cls:"x-dlg-bg",
31795         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31796     });
31797     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31798
31799
31800     if(this.autoScroll !== false && !this.autoTabs){
31801         this.body.setStyle("overflow", "auto");
31802     }
31803
31804     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31805
31806     if(this.closable !== false){
31807         this.el.addClass("x-dlg-closable");
31808         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31809         this.close.on("click", this.closeClick, this);
31810         this.close.addClassOnOver("x-dlg-close-over");
31811     }
31812     if(this.collapsible !== false){
31813         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31814         this.collapseBtn.on("click", this.collapseClick, this);
31815         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31816         this.header.on("dblclick", this.collapseClick, this);
31817     }
31818     if(this.resizable !== false){
31819         this.el.addClass("x-dlg-resizable");
31820         this.resizer = new Roo.Resizable(el, {
31821             minWidth: this.minWidth || 80,
31822             minHeight:this.minHeight || 80,
31823             handles: this.resizeHandles || "all",
31824             pinned: true
31825         });
31826         this.resizer.on("beforeresize", this.beforeResize, this);
31827         this.resizer.on("resize", this.onResize, this);
31828     }
31829     if(this.draggable !== false){
31830         el.addClass("x-dlg-draggable");
31831         if (!this.proxyDrag) {
31832             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31833         }
31834         else {
31835             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31836         }
31837         dd.setHandleElId(this.header.id);
31838         dd.endDrag = this.endMove.createDelegate(this);
31839         dd.startDrag = this.startMove.createDelegate(this);
31840         dd.onDrag = this.onDrag.createDelegate(this);
31841         dd.scroll = false;
31842         this.dd = dd;
31843     }
31844     if(this.modal){
31845         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31846         this.mask.enableDisplayMode("block");
31847         this.mask.hide();
31848         this.el.addClass("x-dlg-modal");
31849     }
31850     if(this.shadow){
31851         this.shadow = new Roo.Shadow({
31852             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31853             offset : this.shadowOffset
31854         });
31855     }else{
31856         this.shadowOffset = 0;
31857     }
31858     if(Roo.useShims && this.shim !== false){
31859         this.shim = this.el.createShim();
31860         this.shim.hide = this.hideAction;
31861         this.shim.hide();
31862     }else{
31863         this.shim = false;
31864     }
31865     if(this.autoTabs){
31866         this.initTabs();
31867     }
31868     if (this.buttons) { 
31869         var bts= this.buttons;
31870         this.buttons = [];
31871         Roo.each(bts, function(b) {
31872             this.addButton(b);
31873         }, this);
31874     }
31875     
31876     
31877     this.addEvents({
31878         /**
31879          * @event keydown
31880          * Fires when a key is pressed
31881          * @param {Roo.BasicDialog} this
31882          * @param {Roo.EventObject} e
31883          */
31884         "keydown" : true,
31885         /**
31886          * @event move
31887          * Fires when this dialog is moved by the user.
31888          * @param {Roo.BasicDialog} this
31889          * @param {Number} x The new page X
31890          * @param {Number} y The new page Y
31891          */
31892         "move" : true,
31893         /**
31894          * @event resize
31895          * Fires when this dialog is resized by the user.
31896          * @param {Roo.BasicDialog} this
31897          * @param {Number} width The new width
31898          * @param {Number} height The new height
31899          */
31900         "resize" : true,
31901         /**
31902          * @event beforehide
31903          * Fires before this dialog is hidden.
31904          * @param {Roo.BasicDialog} this
31905          */
31906         "beforehide" : true,
31907         /**
31908          * @event hide
31909          * Fires when this dialog is hidden.
31910          * @param {Roo.BasicDialog} this
31911          */
31912         "hide" : true,
31913         /**
31914          * @event beforeshow
31915          * Fires before this dialog is shown.
31916          * @param {Roo.BasicDialog} this
31917          */
31918         "beforeshow" : true,
31919         /**
31920          * @event show
31921          * Fires when this dialog is shown.
31922          * @param {Roo.BasicDialog} this
31923          */
31924         "show" : true
31925     });
31926     el.on("keydown", this.onKeyDown, this);
31927     el.on("mousedown", this.toFront, this);
31928     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31929     this.el.hide();
31930     Roo.DialogManager.register(this);
31931     Roo.BasicDialog.superclass.constructor.call(this);
31932 };
31933
31934 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31935     shadowOffset: Roo.isIE ? 6 : 5,
31936     minHeight: 80,
31937     minWidth: 200,
31938     minButtonWidth: 75,
31939     defaultButton: null,
31940     buttonAlign: "right",
31941     tabTag: 'div',
31942     firstShow: true,
31943
31944     /**
31945      * Sets the dialog title text
31946      * @param {String} text The title text to display
31947      * @return {Roo.BasicDialog} this
31948      */
31949     setTitle : function(text){
31950         this.header.update(text);
31951         return this;
31952     },
31953
31954     // private
31955     closeClick : function(){
31956         this.hide();
31957     },
31958
31959     // private
31960     collapseClick : function(){
31961         this[this.collapsed ? "expand" : "collapse"]();
31962     },
31963
31964     /**
31965      * Collapses the dialog to its minimized state (only the title bar is visible).
31966      * Equivalent to the user clicking the collapse dialog button.
31967      */
31968     collapse : function(){
31969         if(!this.collapsed){
31970             this.collapsed = true;
31971             this.el.addClass("x-dlg-collapsed");
31972             this.restoreHeight = this.el.getHeight();
31973             this.resizeTo(this.el.getWidth(), this.header.getHeight());
31974         }
31975     },
31976
31977     /**
31978      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
31979      * clicking the expand dialog button.
31980      */
31981     expand : function(){
31982         if(this.collapsed){
31983             this.collapsed = false;
31984             this.el.removeClass("x-dlg-collapsed");
31985             this.resizeTo(this.el.getWidth(), this.restoreHeight);
31986         }
31987     },
31988
31989     /**
31990      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
31991      * @return {Roo.TabPanel} The tabs component
31992      */
31993     initTabs : function(){
31994         var tabs = this.getTabs();
31995         while(tabs.getTab(0)){
31996             tabs.removeTab(0);
31997         }
31998         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
31999             var dom = el.dom;
32000             tabs.addTab(Roo.id(dom), dom.title);
32001             dom.title = "";
32002         });
32003         tabs.activate(0);
32004         return tabs;
32005     },
32006
32007     // private
32008     beforeResize : function(){
32009         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32010     },
32011
32012     // private
32013     onResize : function(){
32014         this.refreshSize();
32015         this.syncBodyHeight();
32016         this.adjustAssets();
32017         this.focus();
32018         this.fireEvent("resize", this, this.size.width, this.size.height);
32019     },
32020
32021     // private
32022     onKeyDown : function(e){
32023         if(this.isVisible()){
32024             this.fireEvent("keydown", this, e);
32025         }
32026     },
32027
32028     /**
32029      * Resizes the dialog.
32030      * @param {Number} width
32031      * @param {Number} height
32032      * @return {Roo.BasicDialog} this
32033      */
32034     resizeTo : function(width, height){
32035         this.el.setSize(width, height);
32036         this.size = {width: width, height: height};
32037         this.syncBodyHeight();
32038         if(this.fixedcenter){
32039             this.center();
32040         }
32041         if(this.isVisible()){
32042             this.constrainXY();
32043             this.adjustAssets();
32044         }
32045         this.fireEvent("resize", this, width, height);
32046         return this;
32047     },
32048
32049
32050     /**
32051      * Resizes the dialog to fit the specified content size.
32052      * @param {Number} width
32053      * @param {Number} height
32054      * @return {Roo.BasicDialog} this
32055      */
32056     setContentSize : function(w, h){
32057         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32058         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32059         //if(!this.el.isBorderBox()){
32060             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32061             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32062         //}
32063         if(this.tabs){
32064             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32065             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32066         }
32067         this.resizeTo(w, h);
32068         return this;
32069     },
32070
32071     /**
32072      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32073      * executed in response to a particular key being pressed while the dialog is active.
32074      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32075      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32076      * @param {Function} fn The function to call
32077      * @param {Object} scope (optional) The scope of the function
32078      * @return {Roo.BasicDialog} this
32079      */
32080     addKeyListener : function(key, fn, scope){
32081         var keyCode, shift, ctrl, alt;
32082         if(typeof key == "object" && !(key instanceof Array)){
32083             keyCode = key["key"];
32084             shift = key["shift"];
32085             ctrl = key["ctrl"];
32086             alt = key["alt"];
32087         }else{
32088             keyCode = key;
32089         }
32090         var handler = function(dlg, e){
32091             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32092                 var k = e.getKey();
32093                 if(keyCode instanceof Array){
32094                     for(var i = 0, len = keyCode.length; i < len; i++){
32095                         if(keyCode[i] == k){
32096                           fn.call(scope || window, dlg, k, e);
32097                           return;
32098                         }
32099                     }
32100                 }else{
32101                     if(k == keyCode){
32102                         fn.call(scope || window, dlg, k, e);
32103                     }
32104                 }
32105             }
32106         };
32107         this.on("keydown", handler);
32108         return this;
32109     },
32110
32111     /**
32112      * Returns the TabPanel component (creates it if it doesn't exist).
32113      * Note: If you wish to simply check for the existence of tabs without creating them,
32114      * check for a null 'tabs' property.
32115      * @return {Roo.TabPanel} The tabs component
32116      */
32117     getTabs : function(){
32118         if(!this.tabs){
32119             this.el.addClass("x-dlg-auto-tabs");
32120             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32121             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32122         }
32123         return this.tabs;
32124     },
32125
32126     /**
32127      * Adds a button to the footer section of the dialog.
32128      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32129      * object or a valid Roo.DomHelper element config
32130      * @param {Function} handler The function called when the button is clicked
32131      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32132      * @return {Roo.Button} The new button
32133      */
32134     addButton : function(config, handler, scope){
32135         var dh = Roo.DomHelper;
32136         if(!this.footer){
32137             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32138         }
32139         if(!this.btnContainer){
32140             var tb = this.footer.createChild({
32141
32142                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32143                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32144             }, null, true);
32145             this.btnContainer = tb.firstChild.firstChild.firstChild;
32146         }
32147         var bconfig = {
32148             handler: handler,
32149             scope: scope,
32150             minWidth: this.minButtonWidth,
32151             hideParent:true
32152         };
32153         if(typeof config == "string"){
32154             bconfig.text = config;
32155         }else{
32156             if(config.tag){
32157                 bconfig.dhconfig = config;
32158             }else{
32159                 Roo.apply(bconfig, config);
32160             }
32161         }
32162         var fc = false;
32163         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32164             bconfig.position = Math.max(0, bconfig.position);
32165             fc = this.btnContainer.childNodes[bconfig.position];
32166         }
32167          
32168         var btn = new Roo.Button(
32169             fc ? 
32170                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32171                 : this.btnContainer.appendChild(document.createElement("td")),
32172             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32173             bconfig
32174         );
32175         this.syncBodyHeight();
32176         if(!this.buttons){
32177             /**
32178              * Array of all the buttons that have been added to this dialog via addButton
32179              * @type Array
32180              */
32181             this.buttons = [];
32182         }
32183         this.buttons.push(btn);
32184         return btn;
32185     },
32186
32187     /**
32188      * Sets the default button to be focused when the dialog is displayed.
32189      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32190      * @return {Roo.BasicDialog} this
32191      */
32192     setDefaultButton : function(btn){
32193         this.defaultButton = btn;
32194         return this;
32195     },
32196
32197     // private
32198     getHeaderFooterHeight : function(safe){
32199         var height = 0;
32200         if(this.header){
32201            height += this.header.getHeight();
32202         }
32203         if(this.footer){
32204            var fm = this.footer.getMargins();
32205             height += (this.footer.getHeight()+fm.top+fm.bottom);
32206         }
32207         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32208         height += this.centerBg.getPadding("tb");
32209         return height;
32210     },
32211
32212     // private
32213     syncBodyHeight : function()
32214     {
32215         var bd = this.body, // the text
32216             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32217             bw = this.bwrap;
32218         var height = this.size.height - this.getHeaderFooterHeight(false);
32219         bd.setHeight(height-bd.getMargins("tb"));
32220         var hh = this.header.getHeight();
32221         var h = this.size.height-hh;
32222         cb.setHeight(h);
32223         
32224         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32225         bw.setHeight(h-cb.getPadding("tb"));
32226         
32227         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32228         bd.setWidth(bw.getWidth(true));
32229         if(this.tabs){
32230             this.tabs.syncHeight();
32231             if(Roo.isIE){
32232                 this.tabs.el.repaint();
32233             }
32234         }
32235     },
32236
32237     /**
32238      * Restores the previous state of the dialog if Roo.state is configured.
32239      * @return {Roo.BasicDialog} this
32240      */
32241     restoreState : function(){
32242         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32243         if(box && box.width){
32244             this.xy = [box.x, box.y];
32245             this.resizeTo(box.width, box.height);
32246         }
32247         return this;
32248     },
32249
32250     // private
32251     beforeShow : function(){
32252         this.expand();
32253         if(this.fixedcenter){
32254             this.xy = this.el.getCenterXY(true);
32255         }
32256         if(this.modal){
32257             Roo.get(document.body).addClass("x-body-masked");
32258             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32259             this.mask.show();
32260         }
32261         this.constrainXY();
32262     },
32263
32264     // private
32265     animShow : function(){
32266         var b = Roo.get(this.animateTarget).getBox();
32267         this.proxy.setSize(b.width, b.height);
32268         this.proxy.setLocation(b.x, b.y);
32269         this.proxy.show();
32270         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32271                     true, .35, this.showEl.createDelegate(this));
32272     },
32273
32274     /**
32275      * Shows the dialog.
32276      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32277      * @return {Roo.BasicDialog} this
32278      */
32279     show : function(animateTarget){
32280         if (this.fireEvent("beforeshow", this) === false){
32281             return;
32282         }
32283         if(this.syncHeightBeforeShow){
32284             this.syncBodyHeight();
32285         }else if(this.firstShow){
32286             this.firstShow = false;
32287             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32288         }
32289         this.animateTarget = animateTarget || this.animateTarget;
32290         if(!this.el.isVisible()){
32291             this.beforeShow();
32292             if(this.animateTarget && Roo.get(this.animateTarget)){
32293                 this.animShow();
32294             }else{
32295                 this.showEl();
32296             }
32297         }
32298         return this;
32299     },
32300
32301     // private
32302     showEl : function(){
32303         this.proxy.hide();
32304         this.el.setXY(this.xy);
32305         this.el.show();
32306         this.adjustAssets(true);
32307         this.toFront();
32308         this.focus();
32309         // IE peekaboo bug - fix found by Dave Fenwick
32310         if(Roo.isIE){
32311             this.el.repaint();
32312         }
32313         this.fireEvent("show", this);
32314     },
32315
32316     /**
32317      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32318      * dialog itself will receive focus.
32319      */
32320     focus : function(){
32321         if(this.defaultButton){
32322             this.defaultButton.focus();
32323         }else{
32324             this.focusEl.focus();
32325         }
32326     },
32327
32328     // private
32329     constrainXY : function(){
32330         if(this.constraintoviewport !== false){
32331             if(!this.viewSize){
32332                 if(this.container){
32333                     var s = this.container.getSize();
32334                     this.viewSize = [s.width, s.height];
32335                 }else{
32336                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32337                 }
32338             }
32339             var s = Roo.get(this.container||document).getScroll();
32340
32341             var x = this.xy[0], y = this.xy[1];
32342             var w = this.size.width, h = this.size.height;
32343             var vw = this.viewSize[0], vh = this.viewSize[1];
32344             // only move it if it needs it
32345             var moved = false;
32346             // first validate right/bottom
32347             if(x + w > vw+s.left){
32348                 x = vw - w;
32349                 moved = true;
32350             }
32351             if(y + h > vh+s.top){
32352                 y = vh - h;
32353                 moved = true;
32354             }
32355             // then make sure top/left isn't negative
32356             if(x < s.left){
32357                 x = s.left;
32358                 moved = true;
32359             }
32360             if(y < s.top){
32361                 y = s.top;
32362                 moved = true;
32363             }
32364             if(moved){
32365                 // cache xy
32366                 this.xy = [x, y];
32367                 if(this.isVisible()){
32368                     this.el.setLocation(x, y);
32369                     this.adjustAssets();
32370                 }
32371             }
32372         }
32373     },
32374
32375     // private
32376     onDrag : function(){
32377         if(!this.proxyDrag){
32378             this.xy = this.el.getXY();
32379             this.adjustAssets();
32380         }
32381     },
32382
32383     // private
32384     adjustAssets : function(doShow){
32385         var x = this.xy[0], y = this.xy[1];
32386         var w = this.size.width, h = this.size.height;
32387         if(doShow === true){
32388             if(this.shadow){
32389                 this.shadow.show(this.el);
32390             }
32391             if(this.shim){
32392                 this.shim.show();
32393             }
32394         }
32395         if(this.shadow && this.shadow.isVisible()){
32396             this.shadow.show(this.el);
32397         }
32398         if(this.shim && this.shim.isVisible()){
32399             this.shim.setBounds(x, y, w, h);
32400         }
32401     },
32402
32403     // private
32404     adjustViewport : function(w, h){
32405         if(!w || !h){
32406             w = Roo.lib.Dom.getViewWidth();
32407             h = Roo.lib.Dom.getViewHeight();
32408         }
32409         // cache the size
32410         this.viewSize = [w, h];
32411         if(this.modal && this.mask.isVisible()){
32412             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32413             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32414         }
32415         if(this.isVisible()){
32416             this.constrainXY();
32417         }
32418     },
32419
32420     /**
32421      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32422      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32423      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32424      */
32425     destroy : function(removeEl){
32426         if(this.isVisible()){
32427             this.animateTarget = null;
32428             this.hide();
32429         }
32430         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32431         if(this.tabs){
32432             this.tabs.destroy(removeEl);
32433         }
32434         Roo.destroy(
32435              this.shim,
32436              this.proxy,
32437              this.resizer,
32438              this.close,
32439              this.mask
32440         );
32441         if(this.dd){
32442             this.dd.unreg();
32443         }
32444         if(this.buttons){
32445            for(var i = 0, len = this.buttons.length; i < len; i++){
32446                this.buttons[i].destroy();
32447            }
32448         }
32449         this.el.removeAllListeners();
32450         if(removeEl === true){
32451             this.el.update("");
32452             this.el.remove();
32453         }
32454         Roo.DialogManager.unregister(this);
32455     },
32456
32457     // private
32458     startMove : function(){
32459         if(this.proxyDrag){
32460             this.proxy.show();
32461         }
32462         if(this.constraintoviewport !== false){
32463             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32464         }
32465     },
32466
32467     // private
32468     endMove : function(){
32469         if(!this.proxyDrag){
32470             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32471         }else{
32472             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32473             this.proxy.hide();
32474         }
32475         this.refreshSize();
32476         this.adjustAssets();
32477         this.focus();
32478         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32479     },
32480
32481     /**
32482      * Brings this dialog to the front of any other visible dialogs
32483      * @return {Roo.BasicDialog} this
32484      */
32485     toFront : function(){
32486         Roo.DialogManager.bringToFront(this);
32487         return this;
32488     },
32489
32490     /**
32491      * Sends this dialog to the back (under) of any other visible dialogs
32492      * @return {Roo.BasicDialog} this
32493      */
32494     toBack : function(){
32495         Roo.DialogManager.sendToBack(this);
32496         return this;
32497     },
32498
32499     /**
32500      * Centers this dialog in the viewport
32501      * @return {Roo.BasicDialog} this
32502      */
32503     center : function(){
32504         var xy = this.el.getCenterXY(true);
32505         this.moveTo(xy[0], xy[1]);
32506         return this;
32507     },
32508
32509     /**
32510      * Moves the dialog's top-left corner to the specified point
32511      * @param {Number} x
32512      * @param {Number} y
32513      * @return {Roo.BasicDialog} this
32514      */
32515     moveTo : function(x, y){
32516         this.xy = [x,y];
32517         if(this.isVisible()){
32518             this.el.setXY(this.xy);
32519             this.adjustAssets();
32520         }
32521         return this;
32522     },
32523
32524     /**
32525      * Aligns the dialog to the specified element
32526      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32527      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32528      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32529      * @return {Roo.BasicDialog} this
32530      */
32531     alignTo : function(element, position, offsets){
32532         this.xy = this.el.getAlignToXY(element, position, offsets);
32533         if(this.isVisible()){
32534             this.el.setXY(this.xy);
32535             this.adjustAssets();
32536         }
32537         return this;
32538     },
32539
32540     /**
32541      * Anchors an element to another element and realigns it when the window is resized.
32542      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32543      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32544      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32545      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32546      * is a number, it is used as the buffer delay (defaults to 50ms).
32547      * @return {Roo.BasicDialog} this
32548      */
32549     anchorTo : function(el, alignment, offsets, monitorScroll){
32550         var action = function(){
32551             this.alignTo(el, alignment, offsets);
32552         };
32553         Roo.EventManager.onWindowResize(action, this);
32554         var tm = typeof monitorScroll;
32555         if(tm != 'undefined'){
32556             Roo.EventManager.on(window, 'scroll', action, this,
32557                 {buffer: tm == 'number' ? monitorScroll : 50});
32558         }
32559         action.call(this);
32560         return this;
32561     },
32562
32563     /**
32564      * Returns true if the dialog is visible
32565      * @return {Boolean}
32566      */
32567     isVisible : function(){
32568         return this.el.isVisible();
32569     },
32570
32571     // private
32572     animHide : function(callback){
32573         var b = Roo.get(this.animateTarget).getBox();
32574         this.proxy.show();
32575         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32576         this.el.hide();
32577         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32578                     this.hideEl.createDelegate(this, [callback]));
32579     },
32580
32581     /**
32582      * Hides the dialog.
32583      * @param {Function} callback (optional) Function to call when the dialog is hidden
32584      * @return {Roo.BasicDialog} this
32585      */
32586     hide : function(callback){
32587         if (this.fireEvent("beforehide", this) === false){
32588             return;
32589         }
32590         if(this.shadow){
32591             this.shadow.hide();
32592         }
32593         if(this.shim) {
32594           this.shim.hide();
32595         }
32596         // sometimes animateTarget seems to get set.. causing problems...
32597         // this just double checks..
32598         if(this.animateTarget && Roo.get(this.animateTarget)) {
32599            this.animHide(callback);
32600         }else{
32601             this.el.hide();
32602             this.hideEl(callback);
32603         }
32604         return this;
32605     },
32606
32607     // private
32608     hideEl : function(callback){
32609         this.proxy.hide();
32610         if(this.modal){
32611             this.mask.hide();
32612             Roo.get(document.body).removeClass("x-body-masked");
32613         }
32614         this.fireEvent("hide", this);
32615         if(typeof callback == "function"){
32616             callback();
32617         }
32618     },
32619
32620     // private
32621     hideAction : function(){
32622         this.setLeft("-10000px");
32623         this.setTop("-10000px");
32624         this.setStyle("visibility", "hidden");
32625     },
32626
32627     // private
32628     refreshSize : function(){
32629         this.size = this.el.getSize();
32630         this.xy = this.el.getXY();
32631         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32632     },
32633
32634     // private
32635     // z-index is managed by the DialogManager and may be overwritten at any time
32636     setZIndex : function(index){
32637         if(this.modal){
32638             this.mask.setStyle("z-index", index);
32639         }
32640         if(this.shim){
32641             this.shim.setStyle("z-index", ++index);
32642         }
32643         if(this.shadow){
32644             this.shadow.setZIndex(++index);
32645         }
32646         this.el.setStyle("z-index", ++index);
32647         if(this.proxy){
32648             this.proxy.setStyle("z-index", ++index);
32649         }
32650         if(this.resizer){
32651             this.resizer.proxy.setStyle("z-index", ++index);
32652         }
32653
32654         this.lastZIndex = index;
32655     },
32656
32657     /**
32658      * Returns the element for this dialog
32659      * @return {Roo.Element} The underlying dialog Element
32660      */
32661     getEl : function(){
32662         return this.el;
32663     }
32664 });
32665
32666 /**
32667  * @class Roo.DialogManager
32668  * Provides global access to BasicDialogs that have been created and
32669  * support for z-indexing (layering) multiple open dialogs.
32670  */
32671 Roo.DialogManager = function(){
32672     var list = {};
32673     var accessList = [];
32674     var front = null;
32675
32676     // private
32677     var sortDialogs = function(d1, d2){
32678         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32679     };
32680
32681     // private
32682     var orderDialogs = function(){
32683         accessList.sort(sortDialogs);
32684         var seed = Roo.DialogManager.zseed;
32685         for(var i = 0, len = accessList.length; i < len; i++){
32686             var dlg = accessList[i];
32687             if(dlg){
32688                 dlg.setZIndex(seed + (i*10));
32689             }
32690         }
32691     };
32692
32693     return {
32694         /**
32695          * The starting z-index for BasicDialogs (defaults to 9000)
32696          * @type Number The z-index value
32697          */
32698         zseed : 9000,
32699
32700         // private
32701         register : function(dlg){
32702             list[dlg.id] = dlg;
32703             accessList.push(dlg);
32704         },
32705
32706         // private
32707         unregister : function(dlg){
32708             delete list[dlg.id];
32709             var i=0;
32710             var len=0;
32711             if(!accessList.indexOf){
32712                 for(  i = 0, len = accessList.length; i < len; i++){
32713                     if(accessList[i] == dlg){
32714                         accessList.splice(i, 1);
32715                         return;
32716                     }
32717                 }
32718             }else{
32719                  i = accessList.indexOf(dlg);
32720                 if(i != -1){
32721                     accessList.splice(i, 1);
32722                 }
32723             }
32724         },
32725
32726         /**
32727          * Gets a registered dialog by id
32728          * @param {String/Object} id The id of the dialog or a dialog
32729          * @return {Roo.BasicDialog} this
32730          */
32731         get : function(id){
32732             return typeof id == "object" ? id : list[id];
32733         },
32734
32735         /**
32736          * Brings the specified dialog to the front
32737          * @param {String/Object} dlg The id of the dialog or a dialog
32738          * @return {Roo.BasicDialog} this
32739          */
32740         bringToFront : function(dlg){
32741             dlg = this.get(dlg);
32742             if(dlg != front){
32743                 front = dlg;
32744                 dlg._lastAccess = new Date().getTime();
32745                 orderDialogs();
32746             }
32747             return dlg;
32748         },
32749
32750         /**
32751          * Sends the specified dialog to the back
32752          * @param {String/Object} dlg The id of the dialog or a dialog
32753          * @return {Roo.BasicDialog} this
32754          */
32755         sendToBack : function(dlg){
32756             dlg = this.get(dlg);
32757             dlg._lastAccess = -(new Date().getTime());
32758             orderDialogs();
32759             return dlg;
32760         },
32761
32762         /**
32763          * Hides all dialogs
32764          */
32765         hideAll : function(){
32766             for(var id in list){
32767                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32768                     list[id].hide();
32769                 }
32770             }
32771         }
32772     };
32773 }();
32774
32775 /**
32776  * @class Roo.LayoutDialog
32777  * @extends Roo.BasicDialog
32778  * Dialog which provides adjustments for working with a layout in a Dialog.
32779  * Add your necessary layout config options to the dialog's config.<br>
32780  * Example usage (including a nested layout):
32781  * <pre><code>
32782 if(!dialog){
32783     dialog = new Roo.LayoutDialog("download-dlg", {
32784         modal: true,
32785         width:600,
32786         height:450,
32787         shadow:true,
32788         minWidth:500,
32789         minHeight:350,
32790         autoTabs:true,
32791         proxyDrag:true,
32792         // layout config merges with the dialog config
32793         center:{
32794             tabPosition: "top",
32795             alwaysShowTabs: true
32796         }
32797     });
32798     dialog.addKeyListener(27, dialog.hide, dialog);
32799     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32800     dialog.addButton("Build It!", this.getDownload, this);
32801
32802     // we can even add nested layouts
32803     var innerLayout = new Roo.BorderLayout("dl-inner", {
32804         east: {
32805             initialSize: 200,
32806             autoScroll:true,
32807             split:true
32808         },
32809         center: {
32810             autoScroll:true
32811         }
32812     });
32813     innerLayout.beginUpdate();
32814     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32815     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32816     innerLayout.endUpdate(true);
32817
32818     var layout = dialog.getLayout();
32819     layout.beginUpdate();
32820     layout.add("center", new Roo.ContentPanel("standard-panel",
32821                         {title: "Download the Source", fitToFrame:true}));
32822     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32823                {title: "Build your own roo.js"}));
32824     layout.getRegion("center").showPanel(sp);
32825     layout.endUpdate();
32826 }
32827 </code></pre>
32828     * @constructor
32829     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32830     * @param {Object} config configuration options
32831   */
32832 Roo.LayoutDialog = function(el, cfg){
32833     
32834     var config=  cfg;
32835     if (typeof(cfg) == 'undefined') {
32836         config = Roo.apply({}, el);
32837         // not sure why we use documentElement here.. - it should always be body.
32838         // IE7 borks horribly if we use documentElement.
32839         // webkit also does not like documentElement - it creates a body element...
32840         el = Roo.get( document.body || document.documentElement ).createChild();
32841         //config.autoCreate = true;
32842     }
32843     
32844     
32845     config.autoTabs = false;
32846     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32847     this.body.setStyle({overflow:"hidden", position:"relative"});
32848     this.layout = new Roo.BorderLayout(this.body.dom, config);
32849     this.layout.monitorWindowResize = false;
32850     this.el.addClass("x-dlg-auto-layout");
32851     // fix case when center region overwrites center function
32852     this.center = Roo.BasicDialog.prototype.center;
32853     this.on("show", this.layout.layout, this.layout, true);
32854     if (config.items) {
32855         var xitems = config.items;
32856         delete config.items;
32857         Roo.each(xitems, this.addxtype, this);
32858     }
32859     
32860     
32861 };
32862 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32863     /**
32864      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32865      * @deprecated
32866      */
32867     endUpdate : function(){
32868         this.layout.endUpdate();
32869     },
32870
32871     /**
32872      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32873      *  @deprecated
32874      */
32875     beginUpdate : function(){
32876         this.layout.beginUpdate();
32877     },
32878
32879     /**
32880      * Get the BorderLayout for this dialog
32881      * @return {Roo.BorderLayout}
32882      */
32883     getLayout : function(){
32884         return this.layout;
32885     },
32886
32887     showEl : function(){
32888         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32889         if(Roo.isIE7){
32890             this.layout.layout();
32891         }
32892     },
32893
32894     // private
32895     // Use the syncHeightBeforeShow config option to control this automatically
32896     syncBodyHeight : function(){
32897         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32898         if(this.layout){this.layout.layout();}
32899     },
32900     
32901       /**
32902      * Add an xtype element (actually adds to the layout.)
32903      * @return {Object} xdata xtype object data.
32904      */
32905     
32906     addxtype : function(c) {
32907         return this.layout.addxtype(c);
32908     }
32909 });/*
32910  * Based on:
32911  * Ext JS Library 1.1.1
32912  * Copyright(c) 2006-2007, Ext JS, LLC.
32913  *
32914  * Originally Released Under LGPL - original licence link has changed is not relivant.
32915  *
32916  * Fork - LGPL
32917  * <script type="text/javascript">
32918  */
32919  
32920 /**
32921  * @class Roo.MessageBox
32922  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32923  * Example usage:
32924  *<pre><code>
32925 // Basic alert:
32926 Roo.Msg.alert('Status', 'Changes saved successfully.');
32927
32928 // Prompt for user data:
32929 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32930     if (btn == 'ok'){
32931         // process text value...
32932     }
32933 });
32934
32935 // Show a dialog using config options:
32936 Roo.Msg.show({
32937    title:'Save Changes?',
32938    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32939    buttons: Roo.Msg.YESNOCANCEL,
32940    fn: processResult,
32941    animEl: 'elId'
32942 });
32943 </code></pre>
32944  * @singleton
32945  */
32946 Roo.MessageBox = function(){
32947     var dlg, opt, mask, waitTimer;
32948     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
32949     var buttons, activeTextEl, bwidth;
32950
32951     // private
32952     var handleButton = function(button){
32953         dlg.hide();
32954         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
32955     };
32956
32957     // private
32958     var handleHide = function(){
32959         if(opt && opt.cls){
32960             dlg.el.removeClass(opt.cls);
32961         }
32962         if(waitTimer){
32963             Roo.TaskMgr.stop(waitTimer);
32964             waitTimer = null;
32965         }
32966     };
32967
32968     // private
32969     var updateButtons = function(b){
32970         var width = 0;
32971         if(!b){
32972             buttons["ok"].hide();
32973             buttons["cancel"].hide();
32974             buttons["yes"].hide();
32975             buttons["no"].hide();
32976             dlg.footer.dom.style.display = 'none';
32977             return width;
32978         }
32979         dlg.footer.dom.style.display = '';
32980         for(var k in buttons){
32981             if(typeof buttons[k] != "function"){
32982                 if(b[k]){
32983                     buttons[k].show();
32984                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
32985                     width += buttons[k].el.getWidth()+15;
32986                 }else{
32987                     buttons[k].hide();
32988                 }
32989             }
32990         }
32991         return width;
32992     };
32993
32994     // private
32995     var handleEsc = function(d, k, e){
32996         if(opt && opt.closable !== false){
32997             dlg.hide();
32998         }
32999         if(e){
33000             e.stopEvent();
33001         }
33002     };
33003
33004     return {
33005         /**
33006          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33007          * @return {Roo.BasicDialog} The BasicDialog element
33008          */
33009         getDialog : function(){
33010            if(!dlg){
33011                 dlg = new Roo.BasicDialog("x-msg-box", {
33012                     autoCreate : true,
33013                     shadow: true,
33014                     draggable: true,
33015                     resizable:false,
33016                     constraintoviewport:false,
33017                     fixedcenter:true,
33018                     collapsible : false,
33019                     shim:true,
33020                     modal: true,
33021                     width:400, height:100,
33022                     buttonAlign:"center",
33023                     closeClick : function(){
33024                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33025                             handleButton("no");
33026                         }else{
33027                             handleButton("cancel");
33028                         }
33029                     }
33030                 });
33031                 dlg.on("hide", handleHide);
33032                 mask = dlg.mask;
33033                 dlg.addKeyListener(27, handleEsc);
33034                 buttons = {};
33035                 var bt = this.buttonText;
33036                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33037                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33038                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33039                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33040                 bodyEl = dlg.body.createChild({
33041
33042                     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>'
33043                 });
33044                 msgEl = bodyEl.dom.firstChild;
33045                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33046                 textboxEl.enableDisplayMode();
33047                 textboxEl.addKeyListener([10,13], function(){
33048                     if(dlg.isVisible() && opt && opt.buttons){
33049                         if(opt.buttons.ok){
33050                             handleButton("ok");
33051                         }else if(opt.buttons.yes){
33052                             handleButton("yes");
33053                         }
33054                     }
33055                 });
33056                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33057                 textareaEl.enableDisplayMode();
33058                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33059                 progressEl.enableDisplayMode();
33060                 var pf = progressEl.dom.firstChild;
33061                 if (pf) {
33062                     pp = Roo.get(pf.firstChild);
33063                     pp.setHeight(pf.offsetHeight);
33064                 }
33065                 
33066             }
33067             return dlg;
33068         },
33069
33070         /**
33071          * Updates the message box body text
33072          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33073          * the XHTML-compliant non-breaking space character '&amp;#160;')
33074          * @return {Roo.MessageBox} This message box
33075          */
33076         updateText : function(text){
33077             if(!dlg.isVisible() && !opt.width){
33078                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33079             }
33080             msgEl.innerHTML = text || '&#160;';
33081       
33082             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33083             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33084             var w = Math.max(
33085                     Math.min(opt.width || cw , this.maxWidth), 
33086                     Math.max(opt.minWidth || this.minWidth, bwidth)
33087             );
33088             if(opt.prompt){
33089                 activeTextEl.setWidth(w);
33090             }
33091             if(dlg.isVisible()){
33092                 dlg.fixedcenter = false;
33093             }
33094             // to big, make it scroll. = But as usual stupid IE does not support
33095             // !important..
33096             
33097             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33098                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33099                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33100             } else {
33101                 bodyEl.dom.style.height = '';
33102                 bodyEl.dom.style.overflowY = '';
33103             }
33104             if (cw > w) {
33105                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33106             } else {
33107                 bodyEl.dom.style.overflowX = '';
33108             }
33109             
33110             dlg.setContentSize(w, bodyEl.getHeight());
33111             if(dlg.isVisible()){
33112                 dlg.fixedcenter = true;
33113             }
33114             return this;
33115         },
33116
33117         /**
33118          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33119          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33120          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33121          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33122          * @return {Roo.MessageBox} This message box
33123          */
33124         updateProgress : function(value, text){
33125             if(text){
33126                 this.updateText(text);
33127             }
33128             if (pp) { // weird bug on my firefox - for some reason this is not defined
33129                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33130             }
33131             return this;
33132         },        
33133
33134         /**
33135          * Returns true if the message box is currently displayed
33136          * @return {Boolean} True if the message box is visible, else false
33137          */
33138         isVisible : function(){
33139             return dlg && dlg.isVisible();  
33140         },
33141
33142         /**
33143          * Hides the message box if it is displayed
33144          */
33145         hide : function(){
33146             if(this.isVisible()){
33147                 dlg.hide();
33148             }  
33149         },
33150
33151         /**
33152          * Displays a new message box, or reinitializes an existing message box, based on the config options
33153          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33154          * The following config object properties are supported:
33155          * <pre>
33156 Property    Type             Description
33157 ----------  ---------------  ------------------------------------------------------------------------------------
33158 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33159                                    closes (defaults to undefined)
33160 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33161                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33162 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33163                                    progress and wait dialogs will ignore this property and always hide the
33164                                    close button as they can only be closed programmatically.
33165 cls               String           A custom CSS class to apply to the message box element
33166 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33167                                    displayed (defaults to 75)
33168 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33169                                    function will be btn (the name of the button that was clicked, if applicable,
33170                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33171                                    Progress and wait dialogs will ignore this option since they do not respond to
33172                                    user actions and can only be closed programmatically, so any required function
33173                                    should be called by the same code after it closes the dialog.
33174 icon              String           A CSS class that provides a background image to be used as an icon for
33175                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33176 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33177 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33178 modal             Boolean          False to allow user interaction with the page while the message box is
33179                                    displayed (defaults to true)
33180 msg               String           A string that will replace the existing message box body text (defaults
33181                                    to the XHTML-compliant non-breaking space character '&#160;')
33182 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33183 progress          Boolean          True to display a progress bar (defaults to false)
33184 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33185 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33186 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33187 title             String           The title text
33188 value             String           The string value to set into the active textbox element if displayed
33189 wait              Boolean          True to display a progress bar (defaults to false)
33190 width             Number           The width of the dialog in pixels
33191 </pre>
33192          *
33193          * Example usage:
33194          * <pre><code>
33195 Roo.Msg.show({
33196    title: 'Address',
33197    msg: 'Please enter your address:',
33198    width: 300,
33199    buttons: Roo.MessageBox.OKCANCEL,
33200    multiline: true,
33201    fn: saveAddress,
33202    animEl: 'addAddressBtn'
33203 });
33204 </code></pre>
33205          * @param {Object} config Configuration options
33206          * @return {Roo.MessageBox} This message box
33207          */
33208         show : function(options)
33209         {
33210             
33211             // this causes nightmares if you show one dialog after another
33212             // especially on callbacks..
33213              
33214             if(this.isVisible()){
33215                 
33216                 this.hide();
33217                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33218                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33219                 Roo.log("New Dialog Message:" +  options.msg )
33220                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33221                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33222                 
33223             }
33224             var d = this.getDialog();
33225             opt = options;
33226             d.setTitle(opt.title || "&#160;");
33227             d.close.setDisplayed(opt.closable !== false);
33228             activeTextEl = textboxEl;
33229             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33230             if(opt.prompt){
33231                 if(opt.multiline){
33232                     textboxEl.hide();
33233                     textareaEl.show();
33234                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33235                         opt.multiline : this.defaultTextHeight);
33236                     activeTextEl = textareaEl;
33237                 }else{
33238                     textboxEl.show();
33239                     textareaEl.hide();
33240                 }
33241             }else{
33242                 textboxEl.hide();
33243                 textareaEl.hide();
33244             }
33245             progressEl.setDisplayed(opt.progress === true);
33246             this.updateProgress(0);
33247             activeTextEl.dom.value = opt.value || "";
33248             if(opt.prompt){
33249                 dlg.setDefaultButton(activeTextEl);
33250             }else{
33251                 var bs = opt.buttons;
33252                 var db = null;
33253                 if(bs && bs.ok){
33254                     db = buttons["ok"];
33255                 }else if(bs && bs.yes){
33256                     db = buttons["yes"];
33257                 }
33258                 dlg.setDefaultButton(db);
33259             }
33260             bwidth = updateButtons(opt.buttons);
33261             this.updateText(opt.msg);
33262             if(opt.cls){
33263                 d.el.addClass(opt.cls);
33264             }
33265             d.proxyDrag = opt.proxyDrag === true;
33266             d.modal = opt.modal !== false;
33267             d.mask = opt.modal !== false ? mask : false;
33268             if(!d.isVisible()){
33269                 // force it to the end of the z-index stack so it gets a cursor in FF
33270                 document.body.appendChild(dlg.el.dom);
33271                 d.animateTarget = null;
33272                 d.show(options.animEl);
33273             }
33274             return this;
33275         },
33276
33277         /**
33278          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33279          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33280          * and closing the message box when the process is complete.
33281          * @param {String} title The title bar text
33282          * @param {String} msg The message box body text
33283          * @return {Roo.MessageBox} This message box
33284          */
33285         progress : function(title, msg){
33286             this.show({
33287                 title : title,
33288                 msg : msg,
33289                 buttons: false,
33290                 progress:true,
33291                 closable:false,
33292                 minWidth: this.minProgressWidth,
33293                 modal : true
33294             });
33295             return this;
33296         },
33297
33298         /**
33299          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33300          * If a callback function is passed it will be called after the user clicks the button, and the
33301          * id of the button that was clicked will be passed as the only parameter to the callback
33302          * (could also be the top-right close button).
33303          * @param {String} title The title bar text
33304          * @param {String} msg The message box body text
33305          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33306          * @param {Object} scope (optional) The scope of the callback function
33307          * @return {Roo.MessageBox} This message box
33308          */
33309         alert : function(title, msg, fn, scope){
33310             this.show({
33311                 title : title,
33312                 msg : msg,
33313                 buttons: this.OK,
33314                 fn: fn,
33315                 scope : scope,
33316                 modal : true
33317             });
33318             return this;
33319         },
33320
33321         /**
33322          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33323          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33324          * You are responsible for closing the message box when the process is complete.
33325          * @param {String} msg The message box body text
33326          * @param {String} title (optional) The title bar text
33327          * @return {Roo.MessageBox} This message box
33328          */
33329         wait : function(msg, title){
33330             this.show({
33331                 title : title,
33332                 msg : msg,
33333                 buttons: false,
33334                 closable:false,
33335                 progress:true,
33336                 modal:true,
33337                 width:300,
33338                 wait:true
33339             });
33340             waitTimer = Roo.TaskMgr.start({
33341                 run: function(i){
33342                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33343                 },
33344                 interval: 1000
33345             });
33346             return this;
33347         },
33348
33349         /**
33350          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33351          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33352          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33353          * @param {String} title The title bar text
33354          * @param {String} msg The message box body text
33355          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33356          * @param {Object} scope (optional) The scope of the callback function
33357          * @return {Roo.MessageBox} This message box
33358          */
33359         confirm : function(title, msg, fn, scope){
33360             this.show({
33361                 title : title,
33362                 msg : msg,
33363                 buttons: this.YESNO,
33364                 fn: fn,
33365                 scope : scope,
33366                 modal : true
33367             });
33368             return this;
33369         },
33370
33371         /**
33372          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33373          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33374          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33375          * (could also be the top-right close button) and the text that was entered will be passed as the two
33376          * parameters to the callback.
33377          * @param {String} title The title bar text
33378          * @param {String} msg The message box body text
33379          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33380          * @param {Object} scope (optional) The scope of the callback function
33381          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33382          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33383          * @return {Roo.MessageBox} This message box
33384          */
33385         prompt : function(title, msg, fn, scope, multiline){
33386             this.show({
33387                 title : title,
33388                 msg : msg,
33389                 buttons: this.OKCANCEL,
33390                 fn: fn,
33391                 minWidth:250,
33392                 scope : scope,
33393                 prompt:true,
33394                 multiline: multiline,
33395                 modal : true
33396             });
33397             return this;
33398         },
33399
33400         /**
33401          * Button config that displays a single OK button
33402          * @type Object
33403          */
33404         OK : {ok:true},
33405         /**
33406          * Button config that displays Yes and No buttons
33407          * @type Object
33408          */
33409         YESNO : {yes:true, no:true},
33410         /**
33411          * Button config that displays OK and Cancel buttons
33412          * @type Object
33413          */
33414         OKCANCEL : {ok:true, cancel:true},
33415         /**
33416          * Button config that displays Yes, No and Cancel buttons
33417          * @type Object
33418          */
33419         YESNOCANCEL : {yes:true, no:true, cancel:true},
33420
33421         /**
33422          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33423          * @type Number
33424          */
33425         defaultTextHeight : 75,
33426         /**
33427          * The maximum width in pixels of the message box (defaults to 600)
33428          * @type Number
33429          */
33430         maxWidth : 600,
33431         /**
33432          * The minimum width in pixels of the message box (defaults to 100)
33433          * @type Number
33434          */
33435         minWidth : 100,
33436         /**
33437          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33438          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33439          * @type Number
33440          */
33441         minProgressWidth : 250,
33442         /**
33443          * An object containing the default button text strings that can be overriden for localized language support.
33444          * Supported properties are: ok, cancel, yes and no.
33445          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33446          * @type Object
33447          */
33448         buttonText : {
33449             ok : "OK",
33450             cancel : "Cancel",
33451             yes : "Yes",
33452             no : "No"
33453         }
33454     };
33455 }();
33456
33457 /**
33458  * Shorthand for {@link Roo.MessageBox}
33459  */
33460 Roo.Msg = Roo.MessageBox;/*
33461  * Based on:
33462  * Ext JS Library 1.1.1
33463  * Copyright(c) 2006-2007, Ext JS, LLC.
33464  *
33465  * Originally Released Under LGPL - original licence link has changed is not relivant.
33466  *
33467  * Fork - LGPL
33468  * <script type="text/javascript">
33469  */
33470 /**
33471  * @class Roo.QuickTips
33472  * Provides attractive and customizable tooltips for any element.
33473  * @singleton
33474  */
33475 Roo.QuickTips = function(){
33476     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33477     var ce, bd, xy, dd;
33478     var visible = false, disabled = true, inited = false;
33479     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33480     
33481     var onOver = function(e){
33482         if(disabled){
33483             return;
33484         }
33485         var t = e.getTarget();
33486         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33487             return;
33488         }
33489         if(ce && t == ce.el){
33490             clearTimeout(hideProc);
33491             return;
33492         }
33493         if(t && tagEls[t.id]){
33494             tagEls[t.id].el = t;
33495             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33496             return;
33497         }
33498         var ttp, et = Roo.fly(t);
33499         var ns = cfg.namespace;
33500         if(tm.interceptTitles && t.title){
33501             ttp = t.title;
33502             t.qtip = ttp;
33503             t.removeAttribute("title");
33504             e.preventDefault();
33505         }else{
33506             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33507         }
33508         if(ttp){
33509             showProc = show.defer(tm.showDelay, tm, [{
33510                 el: t, 
33511                 text: ttp, 
33512                 width: et.getAttributeNS(ns, cfg.width),
33513                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33514                 title: et.getAttributeNS(ns, cfg.title),
33515                     cls: et.getAttributeNS(ns, cfg.cls)
33516             }]);
33517         }
33518     };
33519     
33520     var onOut = function(e){
33521         clearTimeout(showProc);
33522         var t = e.getTarget();
33523         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33524             hideProc = setTimeout(hide, tm.hideDelay);
33525         }
33526     };
33527     
33528     var onMove = function(e){
33529         if(disabled){
33530             return;
33531         }
33532         xy = e.getXY();
33533         xy[1] += 18;
33534         if(tm.trackMouse && ce){
33535             el.setXY(xy);
33536         }
33537     };
33538     
33539     var onDown = function(e){
33540         clearTimeout(showProc);
33541         clearTimeout(hideProc);
33542         if(!e.within(el)){
33543             if(tm.hideOnClick){
33544                 hide();
33545                 tm.disable();
33546                 tm.enable.defer(100, tm);
33547             }
33548         }
33549     };
33550     
33551     var getPad = function(){
33552         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33553     };
33554
33555     var show = function(o){
33556         if(disabled){
33557             return;
33558         }
33559         clearTimeout(dismissProc);
33560         ce = o;
33561         if(removeCls){ // in case manually hidden
33562             el.removeClass(removeCls);
33563             removeCls = null;
33564         }
33565         if(ce.cls){
33566             el.addClass(ce.cls);
33567             removeCls = ce.cls;
33568         }
33569         if(ce.title){
33570             tipTitle.update(ce.title);
33571             tipTitle.show();
33572         }else{
33573             tipTitle.update('');
33574             tipTitle.hide();
33575         }
33576         el.dom.style.width  = tm.maxWidth+'px';
33577         //tipBody.dom.style.width = '';
33578         tipBodyText.update(o.text);
33579         var p = getPad(), w = ce.width;
33580         if(!w){
33581             var td = tipBodyText.dom;
33582             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33583             if(aw > tm.maxWidth){
33584                 w = tm.maxWidth;
33585             }else if(aw < tm.minWidth){
33586                 w = tm.minWidth;
33587             }else{
33588                 w = aw;
33589             }
33590         }
33591         //tipBody.setWidth(w);
33592         el.setWidth(parseInt(w, 10) + p);
33593         if(ce.autoHide === false){
33594             close.setDisplayed(true);
33595             if(dd){
33596                 dd.unlock();
33597             }
33598         }else{
33599             close.setDisplayed(false);
33600             if(dd){
33601                 dd.lock();
33602             }
33603         }
33604         if(xy){
33605             el.avoidY = xy[1]-18;
33606             el.setXY(xy);
33607         }
33608         if(tm.animate){
33609             el.setOpacity(.1);
33610             el.setStyle("visibility", "visible");
33611             el.fadeIn({callback: afterShow});
33612         }else{
33613             afterShow();
33614         }
33615     };
33616     
33617     var afterShow = function(){
33618         if(ce){
33619             el.show();
33620             esc.enable();
33621             if(tm.autoDismiss && ce.autoHide !== false){
33622                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33623             }
33624         }
33625     };
33626     
33627     var hide = function(noanim){
33628         clearTimeout(dismissProc);
33629         clearTimeout(hideProc);
33630         ce = null;
33631         if(el.isVisible()){
33632             esc.disable();
33633             if(noanim !== true && tm.animate){
33634                 el.fadeOut({callback: afterHide});
33635             }else{
33636                 afterHide();
33637             } 
33638         }
33639     };
33640     
33641     var afterHide = function(){
33642         el.hide();
33643         if(removeCls){
33644             el.removeClass(removeCls);
33645             removeCls = null;
33646         }
33647     };
33648     
33649     return {
33650         /**
33651         * @cfg {Number} minWidth
33652         * The minimum width of the quick tip (defaults to 40)
33653         */
33654        minWidth : 40,
33655         /**
33656         * @cfg {Number} maxWidth
33657         * The maximum width of the quick tip (defaults to 300)
33658         */
33659        maxWidth : 300,
33660         /**
33661         * @cfg {Boolean} interceptTitles
33662         * True to automatically use the element's DOM title value if available (defaults to false)
33663         */
33664        interceptTitles : false,
33665         /**
33666         * @cfg {Boolean} trackMouse
33667         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33668         */
33669        trackMouse : false,
33670         /**
33671         * @cfg {Boolean} hideOnClick
33672         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33673         */
33674        hideOnClick : true,
33675         /**
33676         * @cfg {Number} showDelay
33677         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33678         */
33679        showDelay : 500,
33680         /**
33681         * @cfg {Number} hideDelay
33682         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33683         */
33684        hideDelay : 200,
33685         /**
33686         * @cfg {Boolean} autoHide
33687         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33688         * Used in conjunction with hideDelay.
33689         */
33690        autoHide : true,
33691         /**
33692         * @cfg {Boolean}
33693         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33694         * (defaults to true).  Used in conjunction with autoDismissDelay.
33695         */
33696        autoDismiss : true,
33697         /**
33698         * @cfg {Number}
33699         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33700         */
33701        autoDismissDelay : 5000,
33702        /**
33703         * @cfg {Boolean} animate
33704         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33705         */
33706        animate : false,
33707
33708        /**
33709         * @cfg {String} title
33710         * Title text to display (defaults to '').  This can be any valid HTML markup.
33711         */
33712         title: '',
33713        /**
33714         * @cfg {String} text
33715         * Body text to display (defaults to '').  This can be any valid HTML markup.
33716         */
33717         text : '',
33718        /**
33719         * @cfg {String} cls
33720         * A CSS class to apply to the base quick tip element (defaults to '').
33721         */
33722         cls : '',
33723        /**
33724         * @cfg {Number} width
33725         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33726         * minWidth or maxWidth.
33727         */
33728         width : null,
33729
33730     /**
33731      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33732      * or display QuickTips in a page.
33733      */
33734        init : function(){
33735           tm = Roo.QuickTips;
33736           cfg = tm.tagConfig;
33737           if(!inited){
33738               if(!Roo.isReady){ // allow calling of init() before onReady
33739                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33740                   return;
33741               }
33742               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33743               el.fxDefaults = {stopFx: true};
33744               // maximum custom styling
33745               //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>');
33746               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>');              
33747               tipTitle = el.child('h3');
33748               tipTitle.enableDisplayMode("block");
33749               tipBody = el.child('div.x-tip-bd');
33750               tipBodyText = el.child('div.x-tip-bd-inner');
33751               //bdLeft = el.child('div.x-tip-bd-left');
33752               //bdRight = el.child('div.x-tip-bd-right');
33753               close = el.child('div.x-tip-close');
33754               close.enableDisplayMode("block");
33755               close.on("click", hide);
33756               var d = Roo.get(document);
33757               d.on("mousedown", onDown);
33758               d.on("mouseover", onOver);
33759               d.on("mouseout", onOut);
33760               d.on("mousemove", onMove);
33761               esc = d.addKeyListener(27, hide);
33762               esc.disable();
33763               if(Roo.dd.DD){
33764                   dd = el.initDD("default", null, {
33765                       onDrag : function(){
33766                           el.sync();  
33767                       }
33768                   });
33769                   dd.setHandleElId(tipTitle.id);
33770                   dd.lock();
33771               }
33772               inited = true;
33773           }
33774           this.enable(); 
33775        },
33776
33777     /**
33778      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33779      * are supported:
33780      * <pre>
33781 Property    Type                   Description
33782 ----------  ---------------------  ------------------------------------------------------------------------
33783 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33784      * </ul>
33785      * @param {Object} config The config object
33786      */
33787        register : function(config){
33788            var cs = config instanceof Array ? config : arguments;
33789            for(var i = 0, len = cs.length; i < len; i++) {
33790                var c = cs[i];
33791                var target = c.target;
33792                if(target){
33793                    if(target instanceof Array){
33794                        for(var j = 0, jlen = target.length; j < jlen; j++){
33795                            tagEls[target[j]] = c;
33796                        }
33797                    }else{
33798                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33799                    }
33800                }
33801            }
33802        },
33803
33804     /**
33805      * Removes this quick tip from its element and destroys it.
33806      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33807      */
33808        unregister : function(el){
33809            delete tagEls[Roo.id(el)];
33810        },
33811
33812     /**
33813      * Enable this quick tip.
33814      */
33815        enable : function(){
33816            if(inited && disabled){
33817                locks.pop();
33818                if(locks.length < 1){
33819                    disabled = false;
33820                }
33821            }
33822        },
33823
33824     /**
33825      * Disable this quick tip.
33826      */
33827        disable : function(){
33828           disabled = true;
33829           clearTimeout(showProc);
33830           clearTimeout(hideProc);
33831           clearTimeout(dismissProc);
33832           if(ce){
33833               hide(true);
33834           }
33835           locks.push(1);
33836        },
33837
33838     /**
33839      * Returns true if the quick tip is enabled, else false.
33840      */
33841        isEnabled : function(){
33842             return !disabled;
33843        },
33844
33845         // private
33846        tagConfig : {
33847            namespace : "roo", // was ext?? this may break..
33848            alt_namespace : "ext",
33849            attribute : "qtip",
33850            width : "width",
33851            target : "target",
33852            title : "qtitle",
33853            hide : "hide",
33854            cls : "qclass"
33855        }
33856    };
33857 }();
33858
33859 // backwards compat
33860 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33861  * Based on:
33862  * Ext JS Library 1.1.1
33863  * Copyright(c) 2006-2007, Ext JS, LLC.
33864  *
33865  * Originally Released Under LGPL - original licence link has changed is not relivant.
33866  *
33867  * Fork - LGPL
33868  * <script type="text/javascript">
33869  */
33870  
33871
33872 /**
33873  * @class Roo.tree.TreePanel
33874  * @extends Roo.data.Tree
33875
33876  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33877  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33878  * @cfg {Boolean} enableDD true to enable drag and drop
33879  * @cfg {Boolean} enableDrag true to enable just drag
33880  * @cfg {Boolean} enableDrop true to enable just drop
33881  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33882  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33883  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33884  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33885  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33886  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33887  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33888  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33889  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33890  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33891  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33892  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33893  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33894  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33895  * @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>
33896  * @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>
33897  * 
33898  * @constructor
33899  * @param {String/HTMLElement/Element} el The container element
33900  * @param {Object} config
33901  */
33902 Roo.tree.TreePanel = function(el, config){
33903     var root = false;
33904     var loader = false;
33905     if (config.root) {
33906         root = config.root;
33907         delete config.root;
33908     }
33909     if (config.loader) {
33910         loader = config.loader;
33911         delete config.loader;
33912     }
33913     
33914     Roo.apply(this, config);
33915     Roo.tree.TreePanel.superclass.constructor.call(this);
33916     this.el = Roo.get(el);
33917     this.el.addClass('x-tree');
33918     //console.log(root);
33919     if (root) {
33920         this.setRootNode( Roo.factory(root, Roo.tree));
33921     }
33922     if (loader) {
33923         this.loader = Roo.factory(loader, Roo.tree);
33924     }
33925    /**
33926     * Read-only. The id of the container element becomes this TreePanel's id.
33927     */
33928     this.id = this.el.id;
33929     this.addEvents({
33930         /**
33931         * @event beforeload
33932         * Fires before a node is loaded, return false to cancel
33933         * @param {Node} node The node being loaded
33934         */
33935         "beforeload" : true,
33936         /**
33937         * @event load
33938         * Fires when a node is loaded
33939         * @param {Node} node The node that was loaded
33940         */
33941         "load" : true,
33942         /**
33943         * @event textchange
33944         * Fires when the text for a node is changed
33945         * @param {Node} node The node
33946         * @param {String} text The new text
33947         * @param {String} oldText The old text
33948         */
33949         "textchange" : true,
33950         /**
33951         * @event beforeexpand
33952         * Fires before a node is expanded, return false to cancel.
33953         * @param {Node} node The node
33954         * @param {Boolean} deep
33955         * @param {Boolean} anim
33956         */
33957         "beforeexpand" : true,
33958         /**
33959         * @event beforecollapse
33960         * Fires before a node is collapsed, return false to cancel.
33961         * @param {Node} node The node
33962         * @param {Boolean} deep
33963         * @param {Boolean} anim
33964         */
33965         "beforecollapse" : true,
33966         /**
33967         * @event expand
33968         * Fires when a node is expanded
33969         * @param {Node} node The node
33970         */
33971         "expand" : true,
33972         /**
33973         * @event disabledchange
33974         * Fires when the disabled status of a node changes
33975         * @param {Node} node The node
33976         * @param {Boolean} disabled
33977         */
33978         "disabledchange" : true,
33979         /**
33980         * @event collapse
33981         * Fires when a node is collapsed
33982         * @param {Node} node The node
33983         */
33984         "collapse" : true,
33985         /**
33986         * @event beforeclick
33987         * Fires before click processing on a node. Return false to cancel the default action.
33988         * @param {Node} node The node
33989         * @param {Roo.EventObject} e The event object
33990         */
33991         "beforeclick":true,
33992         /**
33993         * @event checkchange
33994         * Fires when a node with a checkbox's checked property changes
33995         * @param {Node} this This node
33996         * @param {Boolean} checked
33997         */
33998         "checkchange":true,
33999         /**
34000         * @event click
34001         * Fires when a node is clicked
34002         * @param {Node} node The node
34003         * @param {Roo.EventObject} e The event object
34004         */
34005         "click":true,
34006         /**
34007         * @event dblclick
34008         * Fires when a node is double clicked
34009         * @param {Node} node The node
34010         * @param {Roo.EventObject} e The event object
34011         */
34012         "dblclick":true,
34013         /**
34014         * @event contextmenu
34015         * Fires when a node is right clicked
34016         * @param {Node} node The node
34017         * @param {Roo.EventObject} e The event object
34018         */
34019         "contextmenu":true,
34020         /**
34021         * @event beforechildrenrendered
34022         * Fires right before the child nodes for a node are rendered
34023         * @param {Node} node The node
34024         */
34025         "beforechildrenrendered":true,
34026         /**
34027         * @event startdrag
34028         * Fires when a node starts being dragged
34029         * @param {Roo.tree.TreePanel} this
34030         * @param {Roo.tree.TreeNode} node
34031         * @param {event} e The raw browser event
34032         */ 
34033        "startdrag" : true,
34034        /**
34035         * @event enddrag
34036         * Fires when a drag operation is complete
34037         * @param {Roo.tree.TreePanel} this
34038         * @param {Roo.tree.TreeNode} node
34039         * @param {event} e The raw browser event
34040         */
34041        "enddrag" : true,
34042        /**
34043         * @event dragdrop
34044         * Fires when a dragged node is dropped on a valid DD target
34045         * @param {Roo.tree.TreePanel} this
34046         * @param {Roo.tree.TreeNode} node
34047         * @param {DD} dd The dd it was dropped on
34048         * @param {event} e The raw browser event
34049         */
34050        "dragdrop" : true,
34051        /**
34052         * @event beforenodedrop
34053         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34054         * passed to handlers has the following properties:<br />
34055         * <ul style="padding:5px;padding-left:16px;">
34056         * <li>tree - The TreePanel</li>
34057         * <li>target - The node being targeted for the drop</li>
34058         * <li>data - The drag data from the drag source</li>
34059         * <li>point - The point of the drop - append, above or below</li>
34060         * <li>source - The drag source</li>
34061         * <li>rawEvent - Raw mouse event</li>
34062         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34063         * to be inserted by setting them on this object.</li>
34064         * <li>cancel - Set this to true to cancel the drop.</li>
34065         * </ul>
34066         * @param {Object} dropEvent
34067         */
34068        "beforenodedrop" : true,
34069        /**
34070         * @event nodedrop
34071         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34072         * passed to handlers has the following properties:<br />
34073         * <ul style="padding:5px;padding-left:16px;">
34074         * <li>tree - The TreePanel</li>
34075         * <li>target - The node being targeted for the drop</li>
34076         * <li>data - The drag data from the drag source</li>
34077         * <li>point - The point of the drop - append, above or below</li>
34078         * <li>source - The drag source</li>
34079         * <li>rawEvent - Raw mouse event</li>
34080         * <li>dropNode - Dropped node(s).</li>
34081         * </ul>
34082         * @param {Object} dropEvent
34083         */
34084        "nodedrop" : true,
34085         /**
34086         * @event nodedragover
34087         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34088         * passed to handlers has the following properties:<br />
34089         * <ul style="padding:5px;padding-left:16px;">
34090         * <li>tree - The TreePanel</li>
34091         * <li>target - The node being targeted for the drop</li>
34092         * <li>data - The drag data from the drag source</li>
34093         * <li>point - The point of the drop - append, above or below</li>
34094         * <li>source - The drag source</li>
34095         * <li>rawEvent - Raw mouse event</li>
34096         * <li>dropNode - Drop node(s) provided by the source.</li>
34097         * <li>cancel - Set this to true to signal drop not allowed.</li>
34098         * </ul>
34099         * @param {Object} dragOverEvent
34100         */
34101        "nodedragover" : true
34102         
34103     });
34104     if(this.singleExpand){
34105        this.on("beforeexpand", this.restrictExpand, this);
34106     }
34107     if (this.editor) {
34108         this.editor.tree = this;
34109         this.editor = Roo.factory(this.editor, Roo.tree);
34110     }
34111     
34112     if (this.selModel) {
34113         this.selModel = Roo.factory(this.selModel, Roo.tree);
34114     }
34115    
34116 };
34117 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34118     rootVisible : true,
34119     animate: Roo.enableFx,
34120     lines : true,
34121     enableDD : false,
34122     hlDrop : Roo.enableFx,
34123   
34124     renderer: false,
34125     
34126     rendererTip: false,
34127     // private
34128     restrictExpand : function(node){
34129         var p = node.parentNode;
34130         if(p){
34131             if(p.expandedChild && p.expandedChild.parentNode == p){
34132                 p.expandedChild.collapse();
34133             }
34134             p.expandedChild = node;
34135         }
34136     },
34137
34138     // private override
34139     setRootNode : function(node){
34140         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34141         if(!this.rootVisible){
34142             node.ui = new Roo.tree.RootTreeNodeUI(node);
34143         }
34144         return node;
34145     },
34146
34147     /**
34148      * Returns the container element for this TreePanel
34149      */
34150     getEl : function(){
34151         return this.el;
34152     },
34153
34154     /**
34155      * Returns the default TreeLoader for this TreePanel
34156      */
34157     getLoader : function(){
34158         return this.loader;
34159     },
34160
34161     /**
34162      * Expand all nodes
34163      */
34164     expandAll : function(){
34165         this.root.expand(true);
34166     },
34167
34168     /**
34169      * Collapse all nodes
34170      */
34171     collapseAll : function(){
34172         this.root.collapse(true);
34173     },
34174
34175     /**
34176      * Returns the selection model used by this TreePanel
34177      */
34178     getSelectionModel : function(){
34179         if(!this.selModel){
34180             this.selModel = new Roo.tree.DefaultSelectionModel();
34181         }
34182         return this.selModel;
34183     },
34184
34185     /**
34186      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34187      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34188      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34189      * @return {Array}
34190      */
34191     getChecked : function(a, startNode){
34192         startNode = startNode || this.root;
34193         var r = [];
34194         var f = function(){
34195             if(this.attributes.checked){
34196                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34197             }
34198         }
34199         startNode.cascade(f);
34200         return r;
34201     },
34202
34203     /**
34204      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34205      * @param {String} path
34206      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34207      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34208      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34209      */
34210     expandPath : function(path, attr, callback){
34211         attr = attr || "id";
34212         var keys = path.split(this.pathSeparator);
34213         var curNode = this.root;
34214         if(curNode.attributes[attr] != keys[1]){ // invalid root
34215             if(callback){
34216                 callback(false, null);
34217             }
34218             return;
34219         }
34220         var index = 1;
34221         var f = function(){
34222             if(++index == keys.length){
34223                 if(callback){
34224                     callback(true, curNode);
34225                 }
34226                 return;
34227             }
34228             var c = curNode.findChild(attr, keys[index]);
34229             if(!c){
34230                 if(callback){
34231                     callback(false, curNode);
34232                 }
34233                 return;
34234             }
34235             curNode = c;
34236             c.expand(false, false, f);
34237         };
34238         curNode.expand(false, false, f);
34239     },
34240
34241     /**
34242      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34243      * @param {String} path
34244      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34245      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34246      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34247      */
34248     selectPath : function(path, attr, callback){
34249         attr = attr || "id";
34250         var keys = path.split(this.pathSeparator);
34251         var v = keys.pop();
34252         if(keys.length > 0){
34253             var f = function(success, node){
34254                 if(success && node){
34255                     var n = node.findChild(attr, v);
34256                     if(n){
34257                         n.select();
34258                         if(callback){
34259                             callback(true, n);
34260                         }
34261                     }else if(callback){
34262                         callback(false, n);
34263                     }
34264                 }else{
34265                     if(callback){
34266                         callback(false, n);
34267                     }
34268                 }
34269             };
34270             this.expandPath(keys.join(this.pathSeparator), attr, f);
34271         }else{
34272             this.root.select();
34273             if(callback){
34274                 callback(true, this.root);
34275             }
34276         }
34277     },
34278
34279     getTreeEl : function(){
34280         return this.el;
34281     },
34282
34283     /**
34284      * Trigger rendering of this TreePanel
34285      */
34286     render : function(){
34287         if (this.innerCt) {
34288             return this; // stop it rendering more than once!!
34289         }
34290         
34291         this.innerCt = this.el.createChild({tag:"ul",
34292                cls:"x-tree-root-ct " +
34293                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34294
34295         if(this.containerScroll){
34296             Roo.dd.ScrollManager.register(this.el);
34297         }
34298         if((this.enableDD || this.enableDrop) && !this.dropZone){
34299            /**
34300             * The dropZone used by this tree if drop is enabled
34301             * @type Roo.tree.TreeDropZone
34302             */
34303              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34304                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34305            });
34306         }
34307         if((this.enableDD || this.enableDrag) && !this.dragZone){
34308            /**
34309             * The dragZone used by this tree if drag is enabled
34310             * @type Roo.tree.TreeDragZone
34311             */
34312             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34313                ddGroup: this.ddGroup || "TreeDD",
34314                scroll: this.ddScroll
34315            });
34316         }
34317         this.getSelectionModel().init(this);
34318         if (!this.root) {
34319             Roo.log("ROOT not set in tree");
34320             return this;
34321         }
34322         this.root.render();
34323         if(!this.rootVisible){
34324             this.root.renderChildren();
34325         }
34326         return this;
34327     }
34328 });/*
34329  * Based on:
34330  * Ext JS Library 1.1.1
34331  * Copyright(c) 2006-2007, Ext JS, LLC.
34332  *
34333  * Originally Released Under LGPL - original licence link has changed is not relivant.
34334  *
34335  * Fork - LGPL
34336  * <script type="text/javascript">
34337  */
34338  
34339
34340 /**
34341  * @class Roo.tree.DefaultSelectionModel
34342  * @extends Roo.util.Observable
34343  * The default single selection for a TreePanel.
34344  * @param {Object} cfg Configuration
34345  */
34346 Roo.tree.DefaultSelectionModel = function(cfg){
34347    this.selNode = null;
34348    
34349    
34350    
34351    this.addEvents({
34352        /**
34353         * @event selectionchange
34354         * Fires when the selected node changes
34355         * @param {DefaultSelectionModel} this
34356         * @param {TreeNode} node the new selection
34357         */
34358        "selectionchange" : true,
34359
34360        /**
34361         * @event beforeselect
34362         * Fires before the selected node changes, return false to cancel the change
34363         * @param {DefaultSelectionModel} this
34364         * @param {TreeNode} node the new selection
34365         * @param {TreeNode} node the old selection
34366         */
34367        "beforeselect" : true
34368    });
34369    
34370     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34371 };
34372
34373 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34374     init : function(tree){
34375         this.tree = tree;
34376         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34377         tree.on("click", this.onNodeClick, this);
34378     },
34379     
34380     onNodeClick : function(node, e){
34381         if (e.ctrlKey && this.selNode == node)  {
34382             this.unselect(node);
34383             return;
34384         }
34385         this.select(node);
34386     },
34387     
34388     /**
34389      * Select a node.
34390      * @param {TreeNode} node The node to select
34391      * @return {TreeNode} The selected node
34392      */
34393     select : function(node){
34394         var last = this.selNode;
34395         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34396             if(last){
34397                 last.ui.onSelectedChange(false);
34398             }
34399             this.selNode = node;
34400             node.ui.onSelectedChange(true);
34401             this.fireEvent("selectionchange", this, node, last);
34402         }
34403         return node;
34404     },
34405     
34406     /**
34407      * Deselect a node.
34408      * @param {TreeNode} node The node to unselect
34409      */
34410     unselect : function(node){
34411         if(this.selNode == node){
34412             this.clearSelections();
34413         }    
34414     },
34415     
34416     /**
34417      * Clear all selections
34418      */
34419     clearSelections : function(){
34420         var n = this.selNode;
34421         if(n){
34422             n.ui.onSelectedChange(false);
34423             this.selNode = null;
34424             this.fireEvent("selectionchange", this, null);
34425         }
34426         return n;
34427     },
34428     
34429     /**
34430      * Get the selected node
34431      * @return {TreeNode} The selected node
34432      */
34433     getSelectedNode : function(){
34434         return this.selNode;    
34435     },
34436     
34437     /**
34438      * Returns true if the node is selected
34439      * @param {TreeNode} node The node to check
34440      * @return {Boolean}
34441      */
34442     isSelected : function(node){
34443         return this.selNode == node;  
34444     },
34445
34446     /**
34447      * Selects the node above the selected node in the tree, intelligently walking the nodes
34448      * @return TreeNode The new selection
34449      */
34450     selectPrevious : function(){
34451         var s = this.selNode || this.lastSelNode;
34452         if(!s){
34453             return null;
34454         }
34455         var ps = s.previousSibling;
34456         if(ps){
34457             if(!ps.isExpanded() || ps.childNodes.length < 1){
34458                 return this.select(ps);
34459             } else{
34460                 var lc = ps.lastChild;
34461                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34462                     lc = lc.lastChild;
34463                 }
34464                 return this.select(lc);
34465             }
34466         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34467             return this.select(s.parentNode);
34468         }
34469         return null;
34470     },
34471
34472     /**
34473      * Selects the node above the selected node in the tree, intelligently walking the nodes
34474      * @return TreeNode The new selection
34475      */
34476     selectNext : function(){
34477         var s = this.selNode || this.lastSelNode;
34478         if(!s){
34479             return null;
34480         }
34481         if(s.firstChild && s.isExpanded()){
34482              return this.select(s.firstChild);
34483          }else if(s.nextSibling){
34484              return this.select(s.nextSibling);
34485          }else if(s.parentNode){
34486             var newS = null;
34487             s.parentNode.bubble(function(){
34488                 if(this.nextSibling){
34489                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34490                     return false;
34491                 }
34492             });
34493             return newS;
34494          }
34495         return null;
34496     },
34497
34498     onKeyDown : function(e){
34499         var s = this.selNode || this.lastSelNode;
34500         // undesirable, but required
34501         var sm = this;
34502         if(!s){
34503             return;
34504         }
34505         var k = e.getKey();
34506         switch(k){
34507              case e.DOWN:
34508                  e.stopEvent();
34509                  this.selectNext();
34510              break;
34511              case e.UP:
34512                  e.stopEvent();
34513                  this.selectPrevious();
34514              break;
34515              case e.RIGHT:
34516                  e.preventDefault();
34517                  if(s.hasChildNodes()){
34518                      if(!s.isExpanded()){
34519                          s.expand();
34520                      }else if(s.firstChild){
34521                          this.select(s.firstChild, e);
34522                      }
34523                  }
34524              break;
34525              case e.LEFT:
34526                  e.preventDefault();
34527                  if(s.hasChildNodes() && s.isExpanded()){
34528                      s.collapse();
34529                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34530                      this.select(s.parentNode, e);
34531                  }
34532              break;
34533         };
34534     }
34535 });
34536
34537 /**
34538  * @class Roo.tree.MultiSelectionModel
34539  * @extends Roo.util.Observable
34540  * Multi selection for a TreePanel.
34541  * @param {Object} cfg Configuration
34542  */
34543 Roo.tree.MultiSelectionModel = function(){
34544    this.selNodes = [];
34545    this.selMap = {};
34546    this.addEvents({
34547        /**
34548         * @event selectionchange
34549         * Fires when the selected nodes change
34550         * @param {MultiSelectionModel} this
34551         * @param {Array} nodes Array of the selected nodes
34552         */
34553        "selectionchange" : true
34554    });
34555    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34556    
34557 };
34558
34559 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34560     init : function(tree){
34561         this.tree = tree;
34562         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34563         tree.on("click", this.onNodeClick, this);
34564     },
34565     
34566     onNodeClick : function(node, e){
34567         this.select(node, e, e.ctrlKey);
34568     },
34569     
34570     /**
34571      * Select a node.
34572      * @param {TreeNode} node The node to select
34573      * @param {EventObject} e (optional) An event associated with the selection
34574      * @param {Boolean} keepExisting True to retain existing selections
34575      * @return {TreeNode} The selected node
34576      */
34577     select : function(node, e, keepExisting){
34578         if(keepExisting !== true){
34579             this.clearSelections(true);
34580         }
34581         if(this.isSelected(node)){
34582             this.lastSelNode = node;
34583             return node;
34584         }
34585         this.selNodes.push(node);
34586         this.selMap[node.id] = node;
34587         this.lastSelNode = node;
34588         node.ui.onSelectedChange(true);
34589         this.fireEvent("selectionchange", this, this.selNodes);
34590         return node;
34591     },
34592     
34593     /**
34594      * Deselect a node.
34595      * @param {TreeNode} node The node to unselect
34596      */
34597     unselect : function(node){
34598         if(this.selMap[node.id]){
34599             node.ui.onSelectedChange(false);
34600             var sn = this.selNodes;
34601             var index = -1;
34602             if(sn.indexOf){
34603                 index = sn.indexOf(node);
34604             }else{
34605                 for(var i = 0, len = sn.length; i < len; i++){
34606                     if(sn[i] == node){
34607                         index = i;
34608                         break;
34609                     }
34610                 }
34611             }
34612             if(index != -1){
34613                 this.selNodes.splice(index, 1);
34614             }
34615             delete this.selMap[node.id];
34616             this.fireEvent("selectionchange", this, this.selNodes);
34617         }
34618     },
34619     
34620     /**
34621      * Clear all selections
34622      */
34623     clearSelections : function(suppressEvent){
34624         var sn = this.selNodes;
34625         if(sn.length > 0){
34626             for(var i = 0, len = sn.length; i < len; i++){
34627                 sn[i].ui.onSelectedChange(false);
34628             }
34629             this.selNodes = [];
34630             this.selMap = {};
34631             if(suppressEvent !== true){
34632                 this.fireEvent("selectionchange", this, this.selNodes);
34633             }
34634         }
34635     },
34636     
34637     /**
34638      * Returns true if the node is selected
34639      * @param {TreeNode} node The node to check
34640      * @return {Boolean}
34641      */
34642     isSelected : function(node){
34643         return this.selMap[node.id] ? true : false;  
34644     },
34645     
34646     /**
34647      * Returns an array of the selected nodes
34648      * @return {Array}
34649      */
34650     getSelectedNodes : function(){
34651         return this.selNodes;    
34652     },
34653
34654     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34655
34656     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34657
34658     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34659 });/*
34660  * Based on:
34661  * Ext JS Library 1.1.1
34662  * Copyright(c) 2006-2007, Ext JS, LLC.
34663  *
34664  * Originally Released Under LGPL - original licence link has changed is not relivant.
34665  *
34666  * Fork - LGPL
34667  * <script type="text/javascript">
34668  */
34669  
34670 /**
34671  * @class Roo.tree.TreeNode
34672  * @extends Roo.data.Node
34673  * @cfg {String} text The text for this node
34674  * @cfg {Boolean} expanded true to start the node expanded
34675  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34676  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34677  * @cfg {Boolean} disabled true to start the node disabled
34678  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34679  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34680  * @cfg {String} cls A css class to be added to the node
34681  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34682  * @cfg {String} href URL of the link used for the node (defaults to #)
34683  * @cfg {String} hrefTarget target frame for the link
34684  * @cfg {String} qtip An Ext QuickTip for the node
34685  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34686  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34687  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34688  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34689  * (defaults to undefined with no checkbox rendered)
34690  * @constructor
34691  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34692  */
34693 Roo.tree.TreeNode = function(attributes){
34694     attributes = attributes || {};
34695     if(typeof attributes == "string"){
34696         attributes = {text: attributes};
34697     }
34698     this.childrenRendered = false;
34699     this.rendered = false;
34700     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34701     this.expanded = attributes.expanded === true;
34702     this.isTarget = attributes.isTarget !== false;
34703     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34704     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34705
34706     /**
34707      * Read-only. The text for this node. To change it use setText().
34708      * @type String
34709      */
34710     this.text = attributes.text;
34711     /**
34712      * True if this node is disabled.
34713      * @type Boolean
34714      */
34715     this.disabled = attributes.disabled === true;
34716
34717     this.addEvents({
34718         /**
34719         * @event textchange
34720         * Fires when the text for this node is changed
34721         * @param {Node} this This node
34722         * @param {String} text The new text
34723         * @param {String} oldText The old text
34724         */
34725         "textchange" : true,
34726         /**
34727         * @event beforeexpand
34728         * Fires before this node is expanded, return false to cancel.
34729         * @param {Node} this This node
34730         * @param {Boolean} deep
34731         * @param {Boolean} anim
34732         */
34733         "beforeexpand" : true,
34734         /**
34735         * @event beforecollapse
34736         * Fires before this node is collapsed, return false to cancel.
34737         * @param {Node} this This node
34738         * @param {Boolean} deep
34739         * @param {Boolean} anim
34740         */
34741         "beforecollapse" : true,
34742         /**
34743         * @event expand
34744         * Fires when this node is expanded
34745         * @param {Node} this This node
34746         */
34747         "expand" : true,
34748         /**
34749         * @event disabledchange
34750         * Fires when the disabled status of this node changes
34751         * @param {Node} this This node
34752         * @param {Boolean} disabled
34753         */
34754         "disabledchange" : true,
34755         /**
34756         * @event collapse
34757         * Fires when this node is collapsed
34758         * @param {Node} this This node
34759         */
34760         "collapse" : true,
34761         /**
34762         * @event beforeclick
34763         * Fires before click processing. Return false to cancel the default action.
34764         * @param {Node} this This node
34765         * @param {Roo.EventObject} e The event object
34766         */
34767         "beforeclick":true,
34768         /**
34769         * @event checkchange
34770         * Fires when a node with a checkbox's checked property changes
34771         * @param {Node} this This node
34772         * @param {Boolean} checked
34773         */
34774         "checkchange":true,
34775         /**
34776         * @event click
34777         * Fires when this node is clicked
34778         * @param {Node} this This node
34779         * @param {Roo.EventObject} e The event object
34780         */
34781         "click":true,
34782         /**
34783         * @event dblclick
34784         * Fires when this node is double clicked
34785         * @param {Node} this This node
34786         * @param {Roo.EventObject} e The event object
34787         */
34788         "dblclick":true,
34789         /**
34790         * @event contextmenu
34791         * Fires when this node is right clicked
34792         * @param {Node} this This node
34793         * @param {Roo.EventObject} e The event object
34794         */
34795         "contextmenu":true,
34796         /**
34797         * @event beforechildrenrendered
34798         * Fires right before the child nodes for this node are rendered
34799         * @param {Node} this This node
34800         */
34801         "beforechildrenrendered":true
34802     });
34803
34804     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34805
34806     /**
34807      * Read-only. The UI for this node
34808      * @type TreeNodeUI
34809      */
34810     this.ui = new uiClass(this);
34811     
34812     // finally support items[]
34813     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34814         return;
34815     }
34816     
34817     
34818     Roo.each(this.attributes.items, function(c) {
34819         this.appendChild(Roo.factory(c,Roo.Tree));
34820     }, this);
34821     delete this.attributes.items;
34822     
34823     
34824     
34825 };
34826 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34827     preventHScroll: true,
34828     /**
34829      * Returns true if this node is expanded
34830      * @return {Boolean}
34831      */
34832     isExpanded : function(){
34833         return this.expanded;
34834     },
34835
34836     /**
34837      * Returns the UI object for this node
34838      * @return {TreeNodeUI}
34839      */
34840     getUI : function(){
34841         return this.ui;
34842     },
34843
34844     // private override
34845     setFirstChild : function(node){
34846         var of = this.firstChild;
34847         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34848         if(this.childrenRendered && of && node != of){
34849             of.renderIndent(true, true);
34850         }
34851         if(this.rendered){
34852             this.renderIndent(true, true);
34853         }
34854     },
34855
34856     // private override
34857     setLastChild : function(node){
34858         var ol = this.lastChild;
34859         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34860         if(this.childrenRendered && ol && node != ol){
34861             ol.renderIndent(true, true);
34862         }
34863         if(this.rendered){
34864             this.renderIndent(true, true);
34865         }
34866     },
34867
34868     // these methods are overridden to provide lazy rendering support
34869     // private override
34870     appendChild : function()
34871     {
34872         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34873         if(node && this.childrenRendered){
34874             node.render();
34875         }
34876         this.ui.updateExpandIcon();
34877         return node;
34878     },
34879
34880     // private override
34881     removeChild : function(node){
34882         this.ownerTree.getSelectionModel().unselect(node);
34883         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34884         // if it's been rendered remove dom node
34885         if(this.childrenRendered){
34886             node.ui.remove();
34887         }
34888         if(this.childNodes.length < 1){
34889             this.collapse(false, false);
34890         }else{
34891             this.ui.updateExpandIcon();
34892         }
34893         if(!this.firstChild) {
34894             this.childrenRendered = false;
34895         }
34896         return node;
34897     },
34898
34899     // private override
34900     insertBefore : function(node, refNode){
34901         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34902         if(newNode && refNode && this.childrenRendered){
34903             node.render();
34904         }
34905         this.ui.updateExpandIcon();
34906         return newNode;
34907     },
34908
34909     /**
34910      * Sets the text for this node
34911      * @param {String} text
34912      */
34913     setText : function(text){
34914         var oldText = this.text;
34915         this.text = text;
34916         this.attributes.text = text;
34917         if(this.rendered){ // event without subscribing
34918             this.ui.onTextChange(this, text, oldText);
34919         }
34920         this.fireEvent("textchange", this, text, oldText);
34921     },
34922
34923     /**
34924      * Triggers selection of this node
34925      */
34926     select : function(){
34927         this.getOwnerTree().getSelectionModel().select(this);
34928     },
34929
34930     /**
34931      * Triggers deselection of this node
34932      */
34933     unselect : function(){
34934         this.getOwnerTree().getSelectionModel().unselect(this);
34935     },
34936
34937     /**
34938      * Returns true if this node is selected
34939      * @return {Boolean}
34940      */
34941     isSelected : function(){
34942         return this.getOwnerTree().getSelectionModel().isSelected(this);
34943     },
34944
34945     /**
34946      * Expand this node.
34947      * @param {Boolean} deep (optional) True to expand all children as well
34948      * @param {Boolean} anim (optional) false to cancel the default animation
34949      * @param {Function} callback (optional) A callback to be called when
34950      * expanding this node completes (does not wait for deep expand to complete).
34951      * Called with 1 parameter, this node.
34952      */
34953     expand : function(deep, anim, callback){
34954         if(!this.expanded){
34955             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
34956                 return;
34957             }
34958             if(!this.childrenRendered){
34959                 this.renderChildren();
34960             }
34961             this.expanded = true;
34962             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
34963                 this.ui.animExpand(function(){
34964                     this.fireEvent("expand", this);
34965                     if(typeof callback == "function"){
34966                         callback(this);
34967                     }
34968                     if(deep === true){
34969                         this.expandChildNodes(true);
34970                     }
34971                 }.createDelegate(this));
34972                 return;
34973             }else{
34974                 this.ui.expand();
34975                 this.fireEvent("expand", this);
34976                 if(typeof callback == "function"){
34977                     callback(this);
34978                 }
34979             }
34980         }else{
34981            if(typeof callback == "function"){
34982                callback(this);
34983            }
34984         }
34985         if(deep === true){
34986             this.expandChildNodes(true);
34987         }
34988     },
34989
34990     isHiddenRoot : function(){
34991         return this.isRoot && !this.getOwnerTree().rootVisible;
34992     },
34993
34994     /**
34995      * Collapse this node.
34996      * @param {Boolean} deep (optional) True to collapse all children as well
34997      * @param {Boolean} anim (optional) false to cancel the default animation
34998      */
34999     collapse : function(deep, anim){
35000         if(this.expanded && !this.isHiddenRoot()){
35001             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35002                 return;
35003             }
35004             this.expanded = false;
35005             if((this.getOwnerTree().animate && anim !== false) || anim){
35006                 this.ui.animCollapse(function(){
35007                     this.fireEvent("collapse", this);
35008                     if(deep === true){
35009                         this.collapseChildNodes(true);
35010                     }
35011                 }.createDelegate(this));
35012                 return;
35013             }else{
35014                 this.ui.collapse();
35015                 this.fireEvent("collapse", this);
35016             }
35017         }
35018         if(deep === true){
35019             var cs = this.childNodes;
35020             for(var i = 0, len = cs.length; i < len; i++) {
35021                 cs[i].collapse(true, false);
35022             }
35023         }
35024     },
35025
35026     // private
35027     delayedExpand : function(delay){
35028         if(!this.expandProcId){
35029             this.expandProcId = this.expand.defer(delay, this);
35030         }
35031     },
35032
35033     // private
35034     cancelExpand : function(){
35035         if(this.expandProcId){
35036             clearTimeout(this.expandProcId);
35037         }
35038         this.expandProcId = false;
35039     },
35040
35041     /**
35042      * Toggles expanded/collapsed state of the node
35043      */
35044     toggle : function(){
35045         if(this.expanded){
35046             this.collapse();
35047         }else{
35048             this.expand();
35049         }
35050     },
35051
35052     /**
35053      * Ensures all parent nodes are expanded
35054      */
35055     ensureVisible : function(callback){
35056         var tree = this.getOwnerTree();
35057         tree.expandPath(this.parentNode.getPath(), false, function(){
35058             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35059             Roo.callback(callback);
35060         }.createDelegate(this));
35061     },
35062
35063     /**
35064      * Expand all child nodes
35065      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35066      */
35067     expandChildNodes : function(deep){
35068         var cs = this.childNodes;
35069         for(var i = 0, len = cs.length; i < len; i++) {
35070                 cs[i].expand(deep);
35071         }
35072     },
35073
35074     /**
35075      * Collapse all child nodes
35076      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35077      */
35078     collapseChildNodes : function(deep){
35079         var cs = this.childNodes;
35080         for(var i = 0, len = cs.length; i < len; i++) {
35081                 cs[i].collapse(deep);
35082         }
35083     },
35084
35085     /**
35086      * Disables this node
35087      */
35088     disable : function(){
35089         this.disabled = true;
35090         this.unselect();
35091         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35092             this.ui.onDisableChange(this, true);
35093         }
35094         this.fireEvent("disabledchange", this, true);
35095     },
35096
35097     /**
35098      * Enables this node
35099      */
35100     enable : function(){
35101         this.disabled = false;
35102         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35103             this.ui.onDisableChange(this, false);
35104         }
35105         this.fireEvent("disabledchange", this, false);
35106     },
35107
35108     // private
35109     renderChildren : function(suppressEvent){
35110         if(suppressEvent !== false){
35111             this.fireEvent("beforechildrenrendered", this);
35112         }
35113         var cs = this.childNodes;
35114         for(var i = 0, len = cs.length; i < len; i++){
35115             cs[i].render(true);
35116         }
35117         this.childrenRendered = true;
35118     },
35119
35120     // private
35121     sort : function(fn, scope){
35122         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35123         if(this.childrenRendered){
35124             var cs = this.childNodes;
35125             for(var i = 0, len = cs.length; i < len; i++){
35126                 cs[i].render(true);
35127             }
35128         }
35129     },
35130
35131     // private
35132     render : function(bulkRender){
35133         this.ui.render(bulkRender);
35134         if(!this.rendered){
35135             this.rendered = true;
35136             if(this.expanded){
35137                 this.expanded = false;
35138                 this.expand(false, false);
35139             }
35140         }
35141     },
35142
35143     // private
35144     renderIndent : function(deep, refresh){
35145         if(refresh){
35146             this.ui.childIndent = null;
35147         }
35148         this.ui.renderIndent();
35149         if(deep === true && this.childrenRendered){
35150             var cs = this.childNodes;
35151             for(var i = 0, len = cs.length; i < len; i++){
35152                 cs[i].renderIndent(true, refresh);
35153             }
35154         }
35155     }
35156 });/*
35157  * Based on:
35158  * Ext JS Library 1.1.1
35159  * Copyright(c) 2006-2007, Ext JS, LLC.
35160  *
35161  * Originally Released Under LGPL - original licence link has changed is not relivant.
35162  *
35163  * Fork - LGPL
35164  * <script type="text/javascript">
35165  */
35166  
35167 /**
35168  * @class Roo.tree.AsyncTreeNode
35169  * @extends Roo.tree.TreeNode
35170  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35171  * @constructor
35172  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35173  */
35174  Roo.tree.AsyncTreeNode = function(config){
35175     this.loaded = false;
35176     this.loading = false;
35177     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35178     /**
35179     * @event beforeload
35180     * Fires before this node is loaded, return false to cancel
35181     * @param {Node} this This node
35182     */
35183     this.addEvents({'beforeload':true, 'load': true});
35184     /**
35185     * @event load
35186     * Fires when this node is loaded
35187     * @param {Node} this This node
35188     */
35189     /**
35190      * The loader used by this node (defaults to using the tree's defined loader)
35191      * @type TreeLoader
35192      * @property loader
35193      */
35194 };
35195 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35196     expand : function(deep, anim, callback){
35197         if(this.loading){ // if an async load is already running, waiting til it's done
35198             var timer;
35199             var f = function(){
35200                 if(!this.loading){ // done loading
35201                     clearInterval(timer);
35202                     this.expand(deep, anim, callback);
35203                 }
35204             }.createDelegate(this);
35205             timer = setInterval(f, 200);
35206             return;
35207         }
35208         if(!this.loaded){
35209             if(this.fireEvent("beforeload", this) === false){
35210                 return;
35211             }
35212             this.loading = true;
35213             this.ui.beforeLoad(this);
35214             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35215             if(loader){
35216                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35217                 return;
35218             }
35219         }
35220         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35221     },
35222     
35223     /**
35224      * Returns true if this node is currently loading
35225      * @return {Boolean}
35226      */
35227     isLoading : function(){
35228         return this.loading;  
35229     },
35230     
35231     loadComplete : function(deep, anim, callback){
35232         this.loading = false;
35233         this.loaded = true;
35234         this.ui.afterLoad(this);
35235         this.fireEvent("load", this);
35236         this.expand(deep, anim, callback);
35237     },
35238     
35239     /**
35240      * Returns true if this node has been loaded
35241      * @return {Boolean}
35242      */
35243     isLoaded : function(){
35244         return this.loaded;
35245     },
35246     
35247     hasChildNodes : function(){
35248         if(!this.isLeaf() && !this.loaded){
35249             return true;
35250         }else{
35251             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35252         }
35253     },
35254
35255     /**
35256      * Trigger a reload for this node
35257      * @param {Function} callback
35258      */
35259     reload : function(callback){
35260         this.collapse(false, false);
35261         while(this.firstChild){
35262             this.removeChild(this.firstChild);
35263         }
35264         this.childrenRendered = false;
35265         this.loaded = false;
35266         if(this.isHiddenRoot()){
35267             this.expanded = false;
35268         }
35269         this.expand(false, false, callback);
35270     }
35271 });/*
35272  * Based on:
35273  * Ext JS Library 1.1.1
35274  * Copyright(c) 2006-2007, Ext JS, LLC.
35275  *
35276  * Originally Released Under LGPL - original licence link has changed is not relivant.
35277  *
35278  * Fork - LGPL
35279  * <script type="text/javascript">
35280  */
35281  
35282 /**
35283  * @class Roo.tree.TreeNodeUI
35284  * @constructor
35285  * @param {Object} node The node to render
35286  * The TreeNode UI implementation is separate from the
35287  * tree implementation. Unless you are customizing the tree UI,
35288  * you should never have to use this directly.
35289  */
35290 Roo.tree.TreeNodeUI = function(node){
35291     this.node = node;
35292     this.rendered = false;
35293     this.animating = false;
35294     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35295 };
35296
35297 Roo.tree.TreeNodeUI.prototype = {
35298     removeChild : function(node){
35299         if(this.rendered){
35300             this.ctNode.removeChild(node.ui.getEl());
35301         }
35302     },
35303
35304     beforeLoad : function(){
35305          this.addClass("x-tree-node-loading");
35306     },
35307
35308     afterLoad : function(){
35309          this.removeClass("x-tree-node-loading");
35310     },
35311
35312     onTextChange : function(node, text, oldText){
35313         if(this.rendered){
35314             this.textNode.innerHTML = text;
35315         }
35316     },
35317
35318     onDisableChange : function(node, state){
35319         this.disabled = state;
35320         if(state){
35321             this.addClass("x-tree-node-disabled");
35322         }else{
35323             this.removeClass("x-tree-node-disabled");
35324         }
35325     },
35326
35327     onSelectedChange : function(state){
35328         if(state){
35329             this.focus();
35330             this.addClass("x-tree-selected");
35331         }else{
35332             //this.blur();
35333             this.removeClass("x-tree-selected");
35334         }
35335     },
35336
35337     onMove : function(tree, node, oldParent, newParent, index, refNode){
35338         this.childIndent = null;
35339         if(this.rendered){
35340             var targetNode = newParent.ui.getContainer();
35341             if(!targetNode){//target not rendered
35342                 this.holder = document.createElement("div");
35343                 this.holder.appendChild(this.wrap);
35344                 return;
35345             }
35346             var insertBefore = refNode ? refNode.ui.getEl() : null;
35347             if(insertBefore){
35348                 targetNode.insertBefore(this.wrap, insertBefore);
35349             }else{
35350                 targetNode.appendChild(this.wrap);
35351             }
35352             this.node.renderIndent(true);
35353         }
35354     },
35355
35356     addClass : function(cls){
35357         if(this.elNode){
35358             Roo.fly(this.elNode).addClass(cls);
35359         }
35360     },
35361
35362     removeClass : function(cls){
35363         if(this.elNode){
35364             Roo.fly(this.elNode).removeClass(cls);
35365         }
35366     },
35367
35368     remove : function(){
35369         if(this.rendered){
35370             this.holder = document.createElement("div");
35371             this.holder.appendChild(this.wrap);
35372         }
35373     },
35374
35375     fireEvent : function(){
35376         return this.node.fireEvent.apply(this.node, arguments);
35377     },
35378
35379     initEvents : function(){
35380         this.node.on("move", this.onMove, this);
35381         var E = Roo.EventManager;
35382         var a = this.anchor;
35383
35384         var el = Roo.fly(a, '_treeui');
35385
35386         if(Roo.isOpera){ // opera render bug ignores the CSS
35387             el.setStyle("text-decoration", "none");
35388         }
35389
35390         el.on("click", this.onClick, this);
35391         el.on("dblclick", this.onDblClick, this);
35392
35393         if(this.checkbox){
35394             Roo.EventManager.on(this.checkbox,
35395                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35396         }
35397
35398         el.on("contextmenu", this.onContextMenu, this);
35399
35400         var icon = Roo.fly(this.iconNode);
35401         icon.on("click", this.onClick, this);
35402         icon.on("dblclick", this.onDblClick, this);
35403         icon.on("contextmenu", this.onContextMenu, this);
35404         E.on(this.ecNode, "click", this.ecClick, this, true);
35405
35406         if(this.node.disabled){
35407             this.addClass("x-tree-node-disabled");
35408         }
35409         if(this.node.hidden){
35410             this.addClass("x-tree-node-disabled");
35411         }
35412         var ot = this.node.getOwnerTree();
35413         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35414         if(dd && (!this.node.isRoot || ot.rootVisible)){
35415             Roo.dd.Registry.register(this.elNode, {
35416                 node: this.node,
35417                 handles: this.getDDHandles(),
35418                 isHandle: false
35419             });
35420         }
35421     },
35422
35423     getDDHandles : function(){
35424         return [this.iconNode, this.textNode];
35425     },
35426
35427     hide : function(){
35428         if(this.rendered){
35429             this.wrap.style.display = "none";
35430         }
35431     },
35432
35433     show : function(){
35434         if(this.rendered){
35435             this.wrap.style.display = "";
35436         }
35437     },
35438
35439     onContextMenu : function(e){
35440         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35441             e.preventDefault();
35442             this.focus();
35443             this.fireEvent("contextmenu", this.node, e);
35444         }
35445     },
35446
35447     onClick : function(e){
35448         if(this.dropping){
35449             e.stopEvent();
35450             return;
35451         }
35452         if(this.fireEvent("beforeclick", this.node, e) !== false){
35453             if(!this.disabled && this.node.attributes.href){
35454                 this.fireEvent("click", this.node, e);
35455                 return;
35456             }
35457             e.preventDefault();
35458             if(this.disabled){
35459                 return;
35460             }
35461
35462             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35463                 this.node.toggle();
35464             }
35465
35466             this.fireEvent("click", this.node, e);
35467         }else{
35468             e.stopEvent();
35469         }
35470     },
35471
35472     onDblClick : function(e){
35473         e.preventDefault();
35474         if(this.disabled){
35475             return;
35476         }
35477         if(this.checkbox){
35478             this.toggleCheck();
35479         }
35480         if(!this.animating && this.node.hasChildNodes()){
35481             this.node.toggle();
35482         }
35483         this.fireEvent("dblclick", this.node, e);
35484     },
35485
35486     onCheckChange : function(){
35487         var checked = this.checkbox.checked;
35488         this.node.attributes.checked = checked;
35489         this.fireEvent('checkchange', this.node, checked);
35490     },
35491
35492     ecClick : function(e){
35493         if(!this.animating && this.node.hasChildNodes()){
35494             this.node.toggle();
35495         }
35496     },
35497
35498     startDrop : function(){
35499         this.dropping = true;
35500     },
35501
35502     // delayed drop so the click event doesn't get fired on a drop
35503     endDrop : function(){
35504        setTimeout(function(){
35505            this.dropping = false;
35506        }.createDelegate(this), 50);
35507     },
35508
35509     expand : function(){
35510         this.updateExpandIcon();
35511         this.ctNode.style.display = "";
35512     },
35513
35514     focus : function(){
35515         if(!this.node.preventHScroll){
35516             try{this.anchor.focus();
35517             }catch(e){}
35518         }else if(!Roo.isIE){
35519             try{
35520                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35521                 var l = noscroll.scrollLeft;
35522                 this.anchor.focus();
35523                 noscroll.scrollLeft = l;
35524             }catch(e){}
35525         }
35526     },
35527
35528     toggleCheck : function(value){
35529         var cb = this.checkbox;
35530         if(cb){
35531             cb.checked = (value === undefined ? !cb.checked : value);
35532         }
35533     },
35534
35535     blur : function(){
35536         try{
35537             this.anchor.blur();
35538         }catch(e){}
35539     },
35540
35541     animExpand : function(callback){
35542         var ct = Roo.get(this.ctNode);
35543         ct.stopFx();
35544         if(!this.node.hasChildNodes()){
35545             this.updateExpandIcon();
35546             this.ctNode.style.display = "";
35547             Roo.callback(callback);
35548             return;
35549         }
35550         this.animating = true;
35551         this.updateExpandIcon();
35552
35553         ct.slideIn('t', {
35554            callback : function(){
35555                this.animating = false;
35556                Roo.callback(callback);
35557             },
35558             scope: this,
35559             duration: this.node.ownerTree.duration || .25
35560         });
35561     },
35562
35563     highlight : function(){
35564         var tree = this.node.getOwnerTree();
35565         Roo.fly(this.wrap).highlight(
35566             tree.hlColor || "C3DAF9",
35567             {endColor: tree.hlBaseColor}
35568         );
35569     },
35570
35571     collapse : function(){
35572         this.updateExpandIcon();
35573         this.ctNode.style.display = "none";
35574     },
35575
35576     animCollapse : function(callback){
35577         var ct = Roo.get(this.ctNode);
35578         ct.enableDisplayMode('block');
35579         ct.stopFx();
35580
35581         this.animating = true;
35582         this.updateExpandIcon();
35583
35584         ct.slideOut('t', {
35585             callback : function(){
35586                this.animating = false;
35587                Roo.callback(callback);
35588             },
35589             scope: this,
35590             duration: this.node.ownerTree.duration || .25
35591         });
35592     },
35593
35594     getContainer : function(){
35595         return this.ctNode;
35596     },
35597
35598     getEl : function(){
35599         return this.wrap;
35600     },
35601
35602     appendDDGhost : function(ghostNode){
35603         ghostNode.appendChild(this.elNode.cloneNode(true));
35604     },
35605
35606     getDDRepairXY : function(){
35607         return Roo.lib.Dom.getXY(this.iconNode);
35608     },
35609
35610     onRender : function(){
35611         this.render();
35612     },
35613
35614     render : function(bulkRender){
35615         var n = this.node, a = n.attributes;
35616         var targetNode = n.parentNode ?
35617               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35618
35619         if(!this.rendered){
35620             this.rendered = true;
35621
35622             this.renderElements(n, a, targetNode, bulkRender);
35623
35624             if(a.qtip){
35625                if(this.textNode.setAttributeNS){
35626                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35627                    if(a.qtipTitle){
35628                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35629                    }
35630                }else{
35631                    this.textNode.setAttribute("ext:qtip", a.qtip);
35632                    if(a.qtipTitle){
35633                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35634                    }
35635                }
35636             }else if(a.qtipCfg){
35637                 a.qtipCfg.target = Roo.id(this.textNode);
35638                 Roo.QuickTips.register(a.qtipCfg);
35639             }
35640             this.initEvents();
35641             if(!this.node.expanded){
35642                 this.updateExpandIcon();
35643             }
35644         }else{
35645             if(bulkRender === true) {
35646                 targetNode.appendChild(this.wrap);
35647             }
35648         }
35649     },
35650
35651     renderElements : function(n, a, targetNode, bulkRender)
35652     {
35653         // add some indent caching, this helps performance when rendering a large tree
35654         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35655         var t = n.getOwnerTree();
35656         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35657         if (typeof(n.attributes.html) != 'undefined') {
35658             txt = n.attributes.html;
35659         }
35660         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35661         var cb = typeof a.checked == 'boolean';
35662         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35663         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35664             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35665             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35666             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35667             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35668             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35669              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35670                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35671             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35672             "</li>"];
35673
35674         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35675             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35676                                 n.nextSibling.ui.getEl(), buf.join(""));
35677         }else{
35678             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35679         }
35680
35681         this.elNode = this.wrap.childNodes[0];
35682         this.ctNode = this.wrap.childNodes[1];
35683         var cs = this.elNode.childNodes;
35684         this.indentNode = cs[0];
35685         this.ecNode = cs[1];
35686         this.iconNode = cs[2];
35687         var index = 3;
35688         if(cb){
35689             this.checkbox = cs[3];
35690             index++;
35691         }
35692         this.anchor = cs[index];
35693         this.textNode = cs[index].firstChild;
35694     },
35695
35696     getAnchor : function(){
35697         return this.anchor;
35698     },
35699
35700     getTextEl : function(){
35701         return this.textNode;
35702     },
35703
35704     getIconEl : function(){
35705         return this.iconNode;
35706     },
35707
35708     isChecked : function(){
35709         return this.checkbox ? this.checkbox.checked : false;
35710     },
35711
35712     updateExpandIcon : function(){
35713         if(this.rendered){
35714             var n = this.node, c1, c2;
35715             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35716             var hasChild = n.hasChildNodes();
35717             if(hasChild){
35718                 if(n.expanded){
35719                     cls += "-minus";
35720                     c1 = "x-tree-node-collapsed";
35721                     c2 = "x-tree-node-expanded";
35722                 }else{
35723                     cls += "-plus";
35724                     c1 = "x-tree-node-expanded";
35725                     c2 = "x-tree-node-collapsed";
35726                 }
35727                 if(this.wasLeaf){
35728                     this.removeClass("x-tree-node-leaf");
35729                     this.wasLeaf = false;
35730                 }
35731                 if(this.c1 != c1 || this.c2 != c2){
35732                     Roo.fly(this.elNode).replaceClass(c1, c2);
35733                     this.c1 = c1; this.c2 = c2;
35734                 }
35735             }else{
35736                 // this changes non-leafs into leafs if they have no children.
35737                 // it's not very rational behaviour..
35738                 
35739                 if(!this.wasLeaf && this.node.leaf){
35740                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35741                     delete this.c1;
35742                     delete this.c2;
35743                     this.wasLeaf = true;
35744                 }
35745             }
35746             var ecc = "x-tree-ec-icon "+cls;
35747             if(this.ecc != ecc){
35748                 this.ecNode.className = ecc;
35749                 this.ecc = ecc;
35750             }
35751         }
35752     },
35753
35754     getChildIndent : function(){
35755         if(!this.childIndent){
35756             var buf = [];
35757             var p = this.node;
35758             while(p){
35759                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35760                     if(!p.isLast()) {
35761                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35762                     } else {
35763                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35764                     }
35765                 }
35766                 p = p.parentNode;
35767             }
35768             this.childIndent = buf.join("");
35769         }
35770         return this.childIndent;
35771     },
35772
35773     renderIndent : function(){
35774         if(this.rendered){
35775             var indent = "";
35776             var p = this.node.parentNode;
35777             if(p){
35778                 indent = p.ui.getChildIndent();
35779             }
35780             if(this.indentMarkup != indent){ // don't rerender if not required
35781                 this.indentNode.innerHTML = indent;
35782                 this.indentMarkup = indent;
35783             }
35784             this.updateExpandIcon();
35785         }
35786     }
35787 };
35788
35789 Roo.tree.RootTreeNodeUI = function(){
35790     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35791 };
35792 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35793     render : function(){
35794         if(!this.rendered){
35795             var targetNode = this.node.ownerTree.innerCt.dom;
35796             this.node.expanded = true;
35797             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35798             this.wrap = this.ctNode = targetNode.firstChild;
35799         }
35800     },
35801     collapse : function(){
35802     },
35803     expand : function(){
35804     }
35805 });/*
35806  * Based on:
35807  * Ext JS Library 1.1.1
35808  * Copyright(c) 2006-2007, Ext JS, LLC.
35809  *
35810  * Originally Released Under LGPL - original licence link has changed is not relivant.
35811  *
35812  * Fork - LGPL
35813  * <script type="text/javascript">
35814  */
35815 /**
35816  * @class Roo.tree.TreeLoader
35817  * @extends Roo.util.Observable
35818  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35819  * nodes from a specified URL. The response must be a javascript Array definition
35820  * who's elements are node definition objects. eg:
35821  * <pre><code>
35822 {  success : true,
35823    data :      [
35824    
35825     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35826     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35827     ]
35828 }
35829
35830
35831 </code></pre>
35832  * <br><br>
35833  * The old style respose with just an array is still supported, but not recommended.
35834  * <br><br>
35835  *
35836  * A server request is sent, and child nodes are loaded only when a node is expanded.
35837  * The loading node's id is passed to the server under the parameter name "node" to
35838  * enable the server to produce the correct child nodes.
35839  * <br><br>
35840  * To pass extra parameters, an event handler may be attached to the "beforeload"
35841  * event, and the parameters specified in the TreeLoader's baseParams property:
35842  * <pre><code>
35843     myTreeLoader.on("beforeload", function(treeLoader, node) {
35844         this.baseParams.category = node.attributes.category;
35845     }, this);
35846 </code></pre><
35847  * This would pass an HTTP parameter called "category" to the server containing
35848  * the value of the Node's "category" attribute.
35849  * @constructor
35850  * Creates a new Treeloader.
35851  * @param {Object} config A config object containing config properties.
35852  */
35853 Roo.tree.TreeLoader = function(config){
35854     this.baseParams = {};
35855     this.requestMethod = "POST";
35856     Roo.apply(this, config);
35857
35858     this.addEvents({
35859     
35860         /**
35861          * @event beforeload
35862          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35863          * @param {Object} This TreeLoader object.
35864          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35865          * @param {Object} callback The callback function specified in the {@link #load} call.
35866          */
35867         beforeload : true,
35868         /**
35869          * @event load
35870          * Fires when the node has been successfuly loaded.
35871          * @param {Object} This TreeLoader object.
35872          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35873          * @param {Object} response The response object containing the data from the server.
35874          */
35875         load : true,
35876         /**
35877          * @event loadexception
35878          * Fires if the network request failed.
35879          * @param {Object} This TreeLoader object.
35880          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35881          * @param {Object} response The response object containing the data from the server.
35882          */
35883         loadexception : true,
35884         /**
35885          * @event create
35886          * Fires before a node is created, enabling you to return custom Node types 
35887          * @param {Object} This TreeLoader object.
35888          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35889          */
35890         create : true
35891     });
35892
35893     Roo.tree.TreeLoader.superclass.constructor.call(this);
35894 };
35895
35896 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35897     /**
35898     * @cfg {String} dataUrl The URL from which to request a Json string which
35899     * specifies an array of node definition object representing the child nodes
35900     * to be loaded.
35901     */
35902     /**
35903     * @cfg {String} requestMethod either GET or POST
35904     * defaults to POST (due to BC)
35905     * to be loaded.
35906     */
35907     /**
35908     * @cfg {Object} baseParams (optional) An object containing properties which
35909     * specify HTTP parameters to be passed to each request for child nodes.
35910     */
35911     /**
35912     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35913     * created by this loader. If the attributes sent by the server have an attribute in this object,
35914     * they take priority.
35915     */
35916     /**
35917     * @cfg {Object} uiProviders (optional) An object containing properties which
35918     * 
35919     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35920     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35921     * <i>uiProvider</i> attribute of a returned child node is a string rather
35922     * than a reference to a TreeNodeUI implementation, this that string value
35923     * is used as a property name in the uiProviders object. You can define the provider named
35924     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35925     */
35926     uiProviders : {},
35927
35928     /**
35929     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35930     * child nodes before loading.
35931     */
35932     clearOnLoad : true,
35933
35934     /**
35935     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35936     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35937     * Grid query { data : [ .....] }
35938     */
35939     
35940     root : false,
35941      /**
35942     * @cfg {String} queryParam (optional) 
35943     * Name of the query as it will be passed on the querystring (defaults to 'node')
35944     * eg. the request will be ?node=[id]
35945     */
35946     
35947     
35948     queryParam: false,
35949     
35950     /**
35951      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
35952      * This is called automatically when a node is expanded, but may be used to reload
35953      * a node (or append new children if the {@link #clearOnLoad} option is false.)
35954      * @param {Roo.tree.TreeNode} node
35955      * @param {Function} callback
35956      */
35957     load : function(node, callback){
35958         if(this.clearOnLoad){
35959             while(node.firstChild){
35960                 node.removeChild(node.firstChild);
35961             }
35962         }
35963         if(node.attributes.children){ // preloaded json children
35964             var cs = node.attributes.children;
35965             for(var i = 0, len = cs.length; i < len; i++){
35966                 node.appendChild(this.createNode(cs[i]));
35967             }
35968             if(typeof callback == "function"){
35969                 callback();
35970             }
35971         }else if(this.dataUrl){
35972             this.requestData(node, callback);
35973         }
35974     },
35975
35976     getParams: function(node){
35977         var buf = [], bp = this.baseParams;
35978         for(var key in bp){
35979             if(typeof bp[key] != "function"){
35980                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
35981             }
35982         }
35983         var n = this.queryParam === false ? 'node' : this.queryParam;
35984         buf.push(n + "=", encodeURIComponent(node.id));
35985         return buf.join("");
35986     },
35987
35988     requestData : function(node, callback){
35989         if(this.fireEvent("beforeload", this, node, callback) !== false){
35990             this.transId = Roo.Ajax.request({
35991                 method:this.requestMethod,
35992                 url: this.dataUrl||this.url,
35993                 success: this.handleResponse,
35994                 failure: this.handleFailure,
35995                 scope: this,
35996                 argument: {callback: callback, node: node},
35997                 params: this.getParams(node)
35998             });
35999         }else{
36000             // if the load is cancelled, make sure we notify
36001             // the node that we are done
36002             if(typeof callback == "function"){
36003                 callback();
36004             }
36005         }
36006     },
36007
36008     isLoading : function(){
36009         return this.transId ? true : false;
36010     },
36011
36012     abort : function(){
36013         if(this.isLoading()){
36014             Roo.Ajax.abort(this.transId);
36015         }
36016     },
36017
36018     // private
36019     createNode : function(attr)
36020     {
36021         // apply baseAttrs, nice idea Corey!
36022         if(this.baseAttrs){
36023             Roo.applyIf(attr, this.baseAttrs);
36024         }
36025         if(this.applyLoader !== false){
36026             attr.loader = this;
36027         }
36028         // uiProvider = depreciated..
36029         
36030         if(typeof(attr.uiProvider) == 'string'){
36031            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36032                 /**  eval:var:attr */ eval(attr.uiProvider);
36033         }
36034         if(typeof(this.uiProviders['default']) != 'undefined') {
36035             attr.uiProvider = this.uiProviders['default'];
36036         }
36037         
36038         this.fireEvent('create', this, attr);
36039         
36040         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36041         return(attr.leaf ?
36042                         new Roo.tree.TreeNode(attr) :
36043                         new Roo.tree.AsyncTreeNode(attr));
36044     },
36045
36046     processResponse : function(response, node, callback)
36047     {
36048         var json = response.responseText;
36049         try {
36050             
36051             var o = Roo.decode(json);
36052             
36053             if (this.root === false && typeof(o.success) != undefined) {
36054                 this.root = 'data'; // the default behaviour for list like data..
36055                 }
36056                 
36057             if (this.root !== false &&  !o.success) {
36058                 // it's a failure condition.
36059                 var a = response.argument;
36060                 this.fireEvent("loadexception", this, a.node, response);
36061                 Roo.log("Load failed - should have a handler really");
36062                 return;
36063             }
36064             
36065             
36066             
36067             if (this.root !== false) {
36068                  o = o[this.root];
36069             }
36070             
36071             for(var i = 0, len = o.length; i < len; i++){
36072                 var n = this.createNode(o[i]);
36073                 if(n){
36074                     node.appendChild(n);
36075                 }
36076             }
36077             if(typeof callback == "function"){
36078                 callback(this, node);
36079             }
36080         }catch(e){
36081             this.handleFailure(response);
36082         }
36083     },
36084
36085     handleResponse : function(response){
36086         this.transId = false;
36087         var a = response.argument;
36088         this.processResponse(response, a.node, a.callback);
36089         this.fireEvent("load", this, a.node, response);
36090     },
36091
36092     handleFailure : function(response)
36093     {
36094         // should handle failure better..
36095         this.transId = false;
36096         var a = response.argument;
36097         this.fireEvent("loadexception", this, a.node, response);
36098         if(typeof a.callback == "function"){
36099             a.callback(this, a.node);
36100         }
36101     }
36102 });/*
36103  * Based on:
36104  * Ext JS Library 1.1.1
36105  * Copyright(c) 2006-2007, Ext JS, LLC.
36106  *
36107  * Originally Released Under LGPL - original licence link has changed is not relivant.
36108  *
36109  * Fork - LGPL
36110  * <script type="text/javascript">
36111  */
36112
36113 /**
36114 * @class Roo.tree.TreeFilter
36115 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36116 * @param {TreePanel} tree
36117 * @param {Object} config (optional)
36118  */
36119 Roo.tree.TreeFilter = function(tree, config){
36120     this.tree = tree;
36121     this.filtered = {};
36122     Roo.apply(this, config);
36123 };
36124
36125 Roo.tree.TreeFilter.prototype = {
36126     clearBlank:false,
36127     reverse:false,
36128     autoClear:false,
36129     remove:false,
36130
36131      /**
36132      * Filter the data by a specific attribute.
36133      * @param {String/RegExp} value Either string that the attribute value
36134      * should start with or a RegExp to test against the attribute
36135      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36136      * @param {TreeNode} startNode (optional) The node to start the filter at.
36137      */
36138     filter : function(value, attr, startNode){
36139         attr = attr || "text";
36140         var f;
36141         if(typeof value == "string"){
36142             var vlen = value.length;
36143             // auto clear empty filter
36144             if(vlen == 0 && this.clearBlank){
36145                 this.clear();
36146                 return;
36147             }
36148             value = value.toLowerCase();
36149             f = function(n){
36150                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36151             };
36152         }else if(value.exec){ // regex?
36153             f = function(n){
36154                 return value.test(n.attributes[attr]);
36155             };
36156         }else{
36157             throw 'Illegal filter type, must be string or regex';
36158         }
36159         this.filterBy(f, null, startNode);
36160         },
36161
36162     /**
36163      * Filter by a function. The passed function will be called with each
36164      * node in the tree (or from the startNode). If the function returns true, the node is kept
36165      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36166      * @param {Function} fn The filter function
36167      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36168      */
36169     filterBy : function(fn, scope, startNode){
36170         startNode = startNode || this.tree.root;
36171         if(this.autoClear){
36172             this.clear();
36173         }
36174         var af = this.filtered, rv = this.reverse;
36175         var f = function(n){
36176             if(n == startNode){
36177                 return true;
36178             }
36179             if(af[n.id]){
36180                 return false;
36181             }
36182             var m = fn.call(scope || n, n);
36183             if(!m || rv){
36184                 af[n.id] = n;
36185                 n.ui.hide();
36186                 return false;
36187             }
36188             return true;
36189         };
36190         startNode.cascade(f);
36191         if(this.remove){
36192            for(var id in af){
36193                if(typeof id != "function"){
36194                    var n = af[id];
36195                    if(n && n.parentNode){
36196                        n.parentNode.removeChild(n);
36197                    }
36198                }
36199            }
36200         }
36201     },
36202
36203     /**
36204      * Clears the current filter. Note: with the "remove" option
36205      * set a filter cannot be cleared.
36206      */
36207     clear : function(){
36208         var t = this.tree;
36209         var af = this.filtered;
36210         for(var id in af){
36211             if(typeof id != "function"){
36212                 var n = af[id];
36213                 if(n){
36214                     n.ui.show();
36215                 }
36216             }
36217         }
36218         this.filtered = {};
36219     }
36220 };
36221 /*
36222  * Based on:
36223  * Ext JS Library 1.1.1
36224  * Copyright(c) 2006-2007, Ext JS, LLC.
36225  *
36226  * Originally Released Under LGPL - original licence link has changed is not relivant.
36227  *
36228  * Fork - LGPL
36229  * <script type="text/javascript">
36230  */
36231  
36232
36233 /**
36234  * @class Roo.tree.TreeSorter
36235  * Provides sorting of nodes in a TreePanel
36236  * 
36237  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36238  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36239  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36240  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36241  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36242  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36243  * @constructor
36244  * @param {TreePanel} tree
36245  * @param {Object} config
36246  */
36247 Roo.tree.TreeSorter = function(tree, config){
36248     Roo.apply(this, config);
36249     tree.on("beforechildrenrendered", this.doSort, this);
36250     tree.on("append", this.updateSort, this);
36251     tree.on("insert", this.updateSort, this);
36252     
36253     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36254     var p = this.property || "text";
36255     var sortType = this.sortType;
36256     var fs = this.folderSort;
36257     var cs = this.caseSensitive === true;
36258     var leafAttr = this.leafAttr || 'leaf';
36259
36260     this.sortFn = function(n1, n2){
36261         if(fs){
36262             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36263                 return 1;
36264             }
36265             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36266                 return -1;
36267             }
36268         }
36269         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36270         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36271         if(v1 < v2){
36272                         return dsc ? +1 : -1;
36273                 }else if(v1 > v2){
36274                         return dsc ? -1 : +1;
36275         }else{
36276                 return 0;
36277         }
36278     };
36279 };
36280
36281 Roo.tree.TreeSorter.prototype = {
36282     doSort : function(node){
36283         node.sort(this.sortFn);
36284     },
36285     
36286     compareNodes : function(n1, n2){
36287         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36288     },
36289     
36290     updateSort : function(tree, node){
36291         if(node.childrenRendered){
36292             this.doSort.defer(1, this, [node]);
36293         }
36294     }
36295 };/*
36296  * Based on:
36297  * Ext JS Library 1.1.1
36298  * Copyright(c) 2006-2007, Ext JS, LLC.
36299  *
36300  * Originally Released Under LGPL - original licence link has changed is not relivant.
36301  *
36302  * Fork - LGPL
36303  * <script type="text/javascript">
36304  */
36305
36306 if(Roo.dd.DropZone){
36307     
36308 Roo.tree.TreeDropZone = function(tree, config){
36309     this.allowParentInsert = false;
36310     this.allowContainerDrop = false;
36311     this.appendOnly = false;
36312     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36313     this.tree = tree;
36314     this.lastInsertClass = "x-tree-no-status";
36315     this.dragOverData = {};
36316 };
36317
36318 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36319     ddGroup : "TreeDD",
36320     scroll:  true,
36321     
36322     expandDelay : 1000,
36323     
36324     expandNode : function(node){
36325         if(node.hasChildNodes() && !node.isExpanded()){
36326             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36327         }
36328     },
36329     
36330     queueExpand : function(node){
36331         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36332     },
36333     
36334     cancelExpand : function(){
36335         if(this.expandProcId){
36336             clearTimeout(this.expandProcId);
36337             this.expandProcId = false;
36338         }
36339     },
36340     
36341     isValidDropPoint : function(n, pt, dd, e, data){
36342         if(!n || !data){ return false; }
36343         var targetNode = n.node;
36344         var dropNode = data.node;
36345         // default drop rules
36346         if(!(targetNode && targetNode.isTarget && pt)){
36347             return false;
36348         }
36349         if(pt == "append" && targetNode.allowChildren === false){
36350             return false;
36351         }
36352         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36353             return false;
36354         }
36355         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36356             return false;
36357         }
36358         // reuse the object
36359         var overEvent = this.dragOverData;
36360         overEvent.tree = this.tree;
36361         overEvent.target = targetNode;
36362         overEvent.data = data;
36363         overEvent.point = pt;
36364         overEvent.source = dd;
36365         overEvent.rawEvent = e;
36366         overEvent.dropNode = dropNode;
36367         overEvent.cancel = false;  
36368         var result = this.tree.fireEvent("nodedragover", overEvent);
36369         return overEvent.cancel === false && result !== false;
36370     },
36371     
36372     getDropPoint : function(e, n, dd)
36373     {
36374         var tn = n.node;
36375         if(tn.isRoot){
36376             return tn.allowChildren !== false ? "append" : false; // always append for root
36377         }
36378         var dragEl = n.ddel;
36379         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36380         var y = Roo.lib.Event.getPageY(e);
36381         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36382         
36383         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36384         var noAppend = tn.allowChildren === false;
36385         if(this.appendOnly || tn.parentNode.allowChildren === false){
36386             return noAppend ? false : "append";
36387         }
36388         var noBelow = false;
36389         if(!this.allowParentInsert){
36390             noBelow = tn.hasChildNodes() && tn.isExpanded();
36391         }
36392         var q = (b - t) / (noAppend ? 2 : 3);
36393         if(y >= t && y < (t + q)){
36394             return "above";
36395         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36396             return "below";
36397         }else{
36398             return "append";
36399         }
36400     },
36401     
36402     onNodeEnter : function(n, dd, e, data)
36403     {
36404         this.cancelExpand();
36405     },
36406     
36407     onNodeOver : function(n, dd, e, data)
36408     {
36409        
36410         var pt = this.getDropPoint(e, n, dd);
36411         var node = n.node;
36412         
36413         // auto node expand check
36414         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36415             this.queueExpand(node);
36416         }else if(pt != "append"){
36417             this.cancelExpand();
36418         }
36419         
36420         // set the insert point style on the target node
36421         var returnCls = this.dropNotAllowed;
36422         if(this.isValidDropPoint(n, pt, dd, e, data)){
36423            if(pt){
36424                var el = n.ddel;
36425                var cls;
36426                if(pt == "above"){
36427                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36428                    cls = "x-tree-drag-insert-above";
36429                }else if(pt == "below"){
36430                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36431                    cls = "x-tree-drag-insert-below";
36432                }else{
36433                    returnCls = "x-tree-drop-ok-append";
36434                    cls = "x-tree-drag-append";
36435                }
36436                if(this.lastInsertClass != cls){
36437                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36438                    this.lastInsertClass = cls;
36439                }
36440            }
36441        }
36442        return returnCls;
36443     },
36444     
36445     onNodeOut : function(n, dd, e, data){
36446         
36447         this.cancelExpand();
36448         this.removeDropIndicators(n);
36449     },
36450     
36451     onNodeDrop : function(n, dd, e, data){
36452         var point = this.getDropPoint(e, n, dd);
36453         var targetNode = n.node;
36454         targetNode.ui.startDrop();
36455         if(!this.isValidDropPoint(n, point, dd, e, data)){
36456             targetNode.ui.endDrop();
36457             return false;
36458         }
36459         // first try to find the drop node
36460         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36461         var dropEvent = {
36462             tree : this.tree,
36463             target: targetNode,
36464             data: data,
36465             point: point,
36466             source: dd,
36467             rawEvent: e,
36468             dropNode: dropNode,
36469             cancel: !dropNode   
36470         };
36471         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36472         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36473             targetNode.ui.endDrop();
36474             return false;
36475         }
36476         // allow target changing
36477         targetNode = dropEvent.target;
36478         if(point == "append" && !targetNode.isExpanded()){
36479             targetNode.expand(false, null, function(){
36480                 this.completeDrop(dropEvent);
36481             }.createDelegate(this));
36482         }else{
36483             this.completeDrop(dropEvent);
36484         }
36485         return true;
36486     },
36487     
36488     completeDrop : function(de){
36489         var ns = de.dropNode, p = de.point, t = de.target;
36490         if(!(ns instanceof Array)){
36491             ns = [ns];
36492         }
36493         var n;
36494         for(var i = 0, len = ns.length; i < len; i++){
36495             n = ns[i];
36496             if(p == "above"){
36497                 t.parentNode.insertBefore(n, t);
36498             }else if(p == "below"){
36499                 t.parentNode.insertBefore(n, t.nextSibling);
36500             }else{
36501                 t.appendChild(n);
36502             }
36503         }
36504         n.ui.focus();
36505         if(this.tree.hlDrop){
36506             n.ui.highlight();
36507         }
36508         t.ui.endDrop();
36509         this.tree.fireEvent("nodedrop", de);
36510     },
36511     
36512     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36513         if(this.tree.hlDrop){
36514             dropNode.ui.focus();
36515             dropNode.ui.highlight();
36516         }
36517         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36518     },
36519     
36520     getTree : function(){
36521         return this.tree;
36522     },
36523     
36524     removeDropIndicators : function(n){
36525         if(n && n.ddel){
36526             var el = n.ddel;
36527             Roo.fly(el).removeClass([
36528                     "x-tree-drag-insert-above",
36529                     "x-tree-drag-insert-below",
36530                     "x-tree-drag-append"]);
36531             this.lastInsertClass = "_noclass";
36532         }
36533     },
36534     
36535     beforeDragDrop : function(target, e, id){
36536         this.cancelExpand();
36537         return true;
36538     },
36539     
36540     afterRepair : function(data){
36541         if(data && Roo.enableFx){
36542             data.node.ui.highlight();
36543         }
36544         this.hideProxy();
36545     } 
36546     
36547 });
36548
36549 }
36550 /*
36551  * Based on:
36552  * Ext JS Library 1.1.1
36553  * Copyright(c) 2006-2007, Ext JS, LLC.
36554  *
36555  * Originally Released Under LGPL - original licence link has changed is not relivant.
36556  *
36557  * Fork - LGPL
36558  * <script type="text/javascript">
36559  */
36560  
36561
36562 if(Roo.dd.DragZone){
36563 Roo.tree.TreeDragZone = function(tree, config){
36564     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36565     this.tree = tree;
36566 };
36567
36568 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36569     ddGroup : "TreeDD",
36570    
36571     onBeforeDrag : function(data, e){
36572         var n = data.node;
36573         return n && n.draggable && !n.disabled;
36574     },
36575      
36576     
36577     onInitDrag : function(e){
36578         var data = this.dragData;
36579         this.tree.getSelectionModel().select(data.node);
36580         this.proxy.update("");
36581         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36582         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36583     },
36584     
36585     getRepairXY : function(e, data){
36586         return data.node.ui.getDDRepairXY();
36587     },
36588     
36589     onEndDrag : function(data, e){
36590         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36591         
36592         
36593     },
36594     
36595     onValidDrop : function(dd, e, id){
36596         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36597         this.hideProxy();
36598     },
36599     
36600     beforeInvalidDrop : function(e, id){
36601         // this scrolls the original position back into view
36602         var sm = this.tree.getSelectionModel();
36603         sm.clearSelections();
36604         sm.select(this.dragData.node);
36605     }
36606 });
36607 }/*
36608  * Based on:
36609  * Ext JS Library 1.1.1
36610  * Copyright(c) 2006-2007, Ext JS, LLC.
36611  *
36612  * Originally Released Under LGPL - original licence link has changed is not relivant.
36613  *
36614  * Fork - LGPL
36615  * <script type="text/javascript">
36616  */
36617 /**
36618  * @class Roo.tree.TreeEditor
36619  * @extends Roo.Editor
36620  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36621  * as the editor field.
36622  * @constructor
36623  * @param {Object} config (used to be the tree panel.)
36624  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36625  * 
36626  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36627  * @cfg {Roo.form.TextField|Object} field The field configuration
36628  *
36629  * 
36630  */
36631 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36632     var tree = config;
36633     var field;
36634     if (oldconfig) { // old style..
36635         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36636     } else {
36637         // new style..
36638         tree = config.tree;
36639         config.field = config.field  || {};
36640         config.field.xtype = 'TextField';
36641         field = Roo.factory(config.field, Roo.form);
36642     }
36643     config = config || {};
36644     
36645     
36646     this.addEvents({
36647         /**
36648          * @event beforenodeedit
36649          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36650          * false from the handler of this event.
36651          * @param {Editor} this
36652          * @param {Roo.tree.Node} node 
36653          */
36654         "beforenodeedit" : true
36655     });
36656     
36657     //Roo.log(config);
36658     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36659
36660     this.tree = tree;
36661
36662     tree.on('beforeclick', this.beforeNodeClick, this);
36663     tree.getTreeEl().on('mousedown', this.hide, this);
36664     this.on('complete', this.updateNode, this);
36665     this.on('beforestartedit', this.fitToTree, this);
36666     this.on('startedit', this.bindScroll, this, {delay:10});
36667     this.on('specialkey', this.onSpecialKey, this);
36668 };
36669
36670 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36671     /**
36672      * @cfg {String} alignment
36673      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36674      */
36675     alignment: "l-l",
36676     // inherit
36677     autoSize: false,
36678     /**
36679      * @cfg {Boolean} hideEl
36680      * True to hide the bound element while the editor is displayed (defaults to false)
36681      */
36682     hideEl : false,
36683     /**
36684      * @cfg {String} cls
36685      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36686      */
36687     cls: "x-small-editor x-tree-editor",
36688     /**
36689      * @cfg {Boolean} shim
36690      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36691      */
36692     shim:false,
36693     // inherit
36694     shadow:"frame",
36695     /**
36696      * @cfg {Number} maxWidth
36697      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36698      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36699      * scroll and client offsets into account prior to each edit.
36700      */
36701     maxWidth: 250,
36702
36703     editDelay : 350,
36704
36705     // private
36706     fitToTree : function(ed, el){
36707         var td = this.tree.getTreeEl().dom, nd = el.dom;
36708         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36709             td.scrollLeft = nd.offsetLeft;
36710         }
36711         var w = Math.min(
36712                 this.maxWidth,
36713                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36714         this.setSize(w, '');
36715         
36716         return this.fireEvent('beforenodeedit', this, this.editNode);
36717         
36718     },
36719
36720     // private
36721     triggerEdit : function(node){
36722         this.completeEdit();
36723         this.editNode = node;
36724         this.startEdit(node.ui.textNode, node.text);
36725     },
36726
36727     // private
36728     bindScroll : function(){
36729         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36730     },
36731
36732     // private
36733     beforeNodeClick : function(node, e){
36734         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36735         this.lastClick = new Date();
36736         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36737             e.stopEvent();
36738             this.triggerEdit(node);
36739             return false;
36740         }
36741         return true;
36742     },
36743
36744     // private
36745     updateNode : function(ed, value){
36746         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36747         this.editNode.setText(value);
36748     },
36749
36750     // private
36751     onHide : function(){
36752         Roo.tree.TreeEditor.superclass.onHide.call(this);
36753         if(this.editNode){
36754             this.editNode.ui.focus();
36755         }
36756     },
36757
36758     // private
36759     onSpecialKey : function(field, e){
36760         var k = e.getKey();
36761         if(k == e.ESC){
36762             e.stopEvent();
36763             this.cancelEdit();
36764         }else if(k == e.ENTER && !e.hasModifier()){
36765             e.stopEvent();
36766             this.completeEdit();
36767         }
36768     }
36769 });//<Script type="text/javascript">
36770 /*
36771  * Based on:
36772  * Ext JS Library 1.1.1
36773  * Copyright(c) 2006-2007, Ext JS, LLC.
36774  *
36775  * Originally Released Under LGPL - original licence link has changed is not relivant.
36776  *
36777  * Fork - LGPL
36778  * <script type="text/javascript">
36779  */
36780  
36781 /**
36782  * Not documented??? - probably should be...
36783  */
36784
36785 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36786     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36787     
36788     renderElements : function(n, a, targetNode, bulkRender){
36789         //consel.log("renderElements?");
36790         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36791
36792         var t = n.getOwnerTree();
36793         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36794         
36795         var cols = t.columns;
36796         var bw = t.borderWidth;
36797         var c = cols[0];
36798         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36799          var cb = typeof a.checked == "boolean";
36800         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36801         var colcls = 'x-t-' + tid + '-c0';
36802         var buf = [
36803             '<li class="x-tree-node">',
36804             
36805                 
36806                 '<div class="x-tree-node-el ', a.cls,'">',
36807                     // extran...
36808                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36809                 
36810                 
36811                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36812                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36813                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36814                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36815                            (a.iconCls ? ' '+a.iconCls : ''),
36816                            '" unselectable="on" />',
36817                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36818                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36819                              
36820                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36821                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36822                             '<span unselectable="on" qtip="' + tx + '">',
36823                              tx,
36824                              '</span></a>' ,
36825                     '</div>',
36826                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36827                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36828                  ];
36829         for(var i = 1, len = cols.length; i < len; i++){
36830             c = cols[i];
36831             colcls = 'x-t-' + tid + '-c' +i;
36832             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36833             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36834                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36835                       "</div>");
36836          }
36837          
36838          buf.push(
36839             '</a>',
36840             '<div class="x-clear"></div></div>',
36841             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36842             "</li>");
36843         
36844         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36845             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36846                                 n.nextSibling.ui.getEl(), buf.join(""));
36847         }else{
36848             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36849         }
36850         var el = this.wrap.firstChild;
36851         this.elRow = el;
36852         this.elNode = el.firstChild;
36853         this.ranchor = el.childNodes[1];
36854         this.ctNode = this.wrap.childNodes[1];
36855         var cs = el.firstChild.childNodes;
36856         this.indentNode = cs[0];
36857         this.ecNode = cs[1];
36858         this.iconNode = cs[2];
36859         var index = 3;
36860         if(cb){
36861             this.checkbox = cs[3];
36862             index++;
36863         }
36864         this.anchor = cs[index];
36865         
36866         this.textNode = cs[index].firstChild;
36867         
36868         //el.on("click", this.onClick, this);
36869         //el.on("dblclick", this.onDblClick, this);
36870         
36871         
36872        // console.log(this);
36873     },
36874     initEvents : function(){
36875         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36876         
36877             
36878         var a = this.ranchor;
36879
36880         var el = Roo.get(a);
36881
36882         if(Roo.isOpera){ // opera render bug ignores the CSS
36883             el.setStyle("text-decoration", "none");
36884         }
36885
36886         el.on("click", this.onClick, this);
36887         el.on("dblclick", this.onDblClick, this);
36888         el.on("contextmenu", this.onContextMenu, this);
36889         
36890     },
36891     
36892     /*onSelectedChange : function(state){
36893         if(state){
36894             this.focus();
36895             this.addClass("x-tree-selected");
36896         }else{
36897             //this.blur();
36898             this.removeClass("x-tree-selected");
36899         }
36900     },*/
36901     addClass : function(cls){
36902         if(this.elRow){
36903             Roo.fly(this.elRow).addClass(cls);
36904         }
36905         
36906     },
36907     
36908     
36909     removeClass : function(cls){
36910         if(this.elRow){
36911             Roo.fly(this.elRow).removeClass(cls);
36912         }
36913     }
36914
36915     
36916     
36917 });//<Script type="text/javascript">
36918
36919 /*
36920  * Based on:
36921  * Ext JS Library 1.1.1
36922  * Copyright(c) 2006-2007, Ext JS, LLC.
36923  *
36924  * Originally Released Under LGPL - original licence link has changed is not relivant.
36925  *
36926  * Fork - LGPL
36927  * <script type="text/javascript">
36928  */
36929  
36930
36931 /**
36932  * @class Roo.tree.ColumnTree
36933  * @extends Roo.data.TreePanel
36934  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36935  * @cfg {int} borderWidth  compined right/left border allowance
36936  * @constructor
36937  * @param {String/HTMLElement/Element} el The container element
36938  * @param {Object} config
36939  */
36940 Roo.tree.ColumnTree =  function(el, config)
36941 {
36942    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36943    this.addEvents({
36944         /**
36945         * @event resize
36946         * Fire this event on a container when it resizes
36947         * @param {int} w Width
36948         * @param {int} h Height
36949         */
36950        "resize" : true
36951     });
36952     this.on('resize', this.onResize, this);
36953 };
36954
36955 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
36956     //lines:false,
36957     
36958     
36959     borderWidth: Roo.isBorderBox ? 0 : 2, 
36960     headEls : false,
36961     
36962     render : function(){
36963         // add the header.....
36964        
36965         Roo.tree.ColumnTree.superclass.render.apply(this);
36966         
36967         this.el.addClass('x-column-tree');
36968         
36969         this.headers = this.el.createChild(
36970             {cls:'x-tree-headers'},this.innerCt.dom);
36971    
36972         var cols = this.columns, c;
36973         var totalWidth = 0;
36974         this.headEls = [];
36975         var  len = cols.length;
36976         for(var i = 0; i < len; i++){
36977              c = cols[i];
36978              totalWidth += c.width;
36979             this.headEls.push(this.headers.createChild({
36980                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
36981                  cn: {
36982                      cls:'x-tree-hd-text',
36983                      html: c.header
36984                  },
36985                  style:'width:'+(c.width-this.borderWidth)+'px;'
36986              }));
36987         }
36988         this.headers.createChild({cls:'x-clear'});
36989         // prevent floats from wrapping when clipped
36990         this.headers.setWidth(totalWidth);
36991         //this.innerCt.setWidth(totalWidth);
36992         this.innerCt.setStyle({ overflow: 'auto' });
36993         this.onResize(this.width, this.height);
36994              
36995         
36996     },
36997     onResize : function(w,h)
36998     {
36999         this.height = h;
37000         this.width = w;
37001         // resize cols..
37002         this.innerCt.setWidth(this.width);
37003         this.innerCt.setHeight(this.height-20);
37004         
37005         // headers...
37006         var cols = this.columns, c;
37007         var totalWidth = 0;
37008         var expEl = false;
37009         var len = cols.length;
37010         for(var i = 0; i < len; i++){
37011             c = cols[i];
37012             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37013                 // it's the expander..
37014                 expEl  = this.headEls[i];
37015                 continue;
37016             }
37017             totalWidth += c.width;
37018             
37019         }
37020         if (expEl) {
37021             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37022         }
37023         this.headers.setWidth(w-20);
37024
37025         
37026         
37027         
37028     }
37029 });
37030 /*
37031  * Based on:
37032  * Ext JS Library 1.1.1
37033  * Copyright(c) 2006-2007, Ext JS, LLC.
37034  *
37035  * Originally Released Under LGPL - original licence link has changed is not relivant.
37036  *
37037  * Fork - LGPL
37038  * <script type="text/javascript">
37039  */
37040  
37041 /**
37042  * @class Roo.menu.Menu
37043  * @extends Roo.util.Observable
37044  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37045  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37046  * @constructor
37047  * Creates a new Menu
37048  * @param {Object} config Configuration options
37049  */
37050 Roo.menu.Menu = function(config){
37051     Roo.apply(this, config);
37052     this.id = this.id || Roo.id();
37053     this.addEvents({
37054         /**
37055          * @event beforeshow
37056          * Fires before this menu is displayed
37057          * @param {Roo.menu.Menu} this
37058          */
37059         beforeshow : true,
37060         /**
37061          * @event beforehide
37062          * Fires before this menu is hidden
37063          * @param {Roo.menu.Menu} this
37064          */
37065         beforehide : true,
37066         /**
37067          * @event show
37068          * Fires after this menu is displayed
37069          * @param {Roo.menu.Menu} this
37070          */
37071         show : true,
37072         /**
37073          * @event hide
37074          * Fires after this menu is hidden
37075          * @param {Roo.menu.Menu} this
37076          */
37077         hide : true,
37078         /**
37079          * @event click
37080          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37081          * @param {Roo.menu.Menu} this
37082          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37083          * @param {Roo.EventObject} e
37084          */
37085         click : true,
37086         /**
37087          * @event mouseover
37088          * Fires when the mouse is hovering over this menu
37089          * @param {Roo.menu.Menu} this
37090          * @param {Roo.EventObject} e
37091          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37092          */
37093         mouseover : true,
37094         /**
37095          * @event mouseout
37096          * Fires when the mouse exits this menu
37097          * @param {Roo.menu.Menu} this
37098          * @param {Roo.EventObject} e
37099          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37100          */
37101         mouseout : true,
37102         /**
37103          * @event itemclick
37104          * Fires when a menu item contained in this menu is clicked
37105          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37106          * @param {Roo.EventObject} e
37107          */
37108         itemclick: true
37109     });
37110     if (this.registerMenu) {
37111         Roo.menu.MenuMgr.register(this);
37112     }
37113     
37114     var mis = this.items;
37115     this.items = new Roo.util.MixedCollection();
37116     if(mis){
37117         this.add.apply(this, mis);
37118     }
37119 };
37120
37121 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37122     /**
37123      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37124      */
37125     minWidth : 120,
37126     /**
37127      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37128      * for bottom-right shadow (defaults to "sides")
37129      */
37130     shadow : "sides",
37131     /**
37132      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37133      * this menu (defaults to "tl-tr?")
37134      */
37135     subMenuAlign : "tl-tr?",
37136     /**
37137      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37138      * relative to its element of origin (defaults to "tl-bl?")
37139      */
37140     defaultAlign : "tl-bl?",
37141     /**
37142      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37143      */
37144     allowOtherMenus : false,
37145     /**
37146      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37147      */
37148     registerMenu : true,
37149
37150     hidden:true,
37151
37152     // private
37153     render : function(){
37154         if(this.el){
37155             return;
37156         }
37157         var el = this.el = new Roo.Layer({
37158             cls: "x-menu",
37159             shadow:this.shadow,
37160             constrain: false,
37161             parentEl: this.parentEl || document.body,
37162             zindex:15000
37163         });
37164
37165         this.keyNav = new Roo.menu.MenuNav(this);
37166
37167         if(this.plain){
37168             el.addClass("x-menu-plain");
37169         }
37170         if(this.cls){
37171             el.addClass(this.cls);
37172         }
37173         // generic focus element
37174         this.focusEl = el.createChild({
37175             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37176         });
37177         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37178         //disabling touch- as it's causing issues ..
37179         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37180         ul.on('click'   , this.onClick, this);
37181         
37182         
37183         ul.on("mouseover", this.onMouseOver, this);
37184         ul.on("mouseout", this.onMouseOut, this);
37185         this.items.each(function(item){
37186             if (item.hidden) {
37187                 return;
37188             }
37189             
37190             var li = document.createElement("li");
37191             li.className = "x-menu-list-item";
37192             ul.dom.appendChild(li);
37193             item.render(li, this);
37194         }, this);
37195         this.ul = ul;
37196         this.autoWidth();
37197     },
37198
37199     // private
37200     autoWidth : function(){
37201         var el = this.el, ul = this.ul;
37202         if(!el){
37203             return;
37204         }
37205         var w = this.width;
37206         if(w){
37207             el.setWidth(w);
37208         }else if(Roo.isIE){
37209             el.setWidth(this.minWidth);
37210             var t = el.dom.offsetWidth; // force recalc
37211             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37212         }
37213     },
37214
37215     // private
37216     delayAutoWidth : function(){
37217         if(this.rendered){
37218             if(!this.awTask){
37219                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37220             }
37221             this.awTask.delay(20);
37222         }
37223     },
37224
37225     // private
37226     findTargetItem : function(e){
37227         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37228         if(t && t.menuItemId){
37229             return this.items.get(t.menuItemId);
37230         }
37231     },
37232
37233     // private
37234     onClick : function(e){
37235         Roo.log("menu.onClick");
37236         var t = this.findTargetItem(e);
37237         if(!t){
37238             return;
37239         }
37240         Roo.log(e);
37241         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37242             if(t == this.activeItem && t.shouldDeactivate(e)){
37243                 this.activeItem.deactivate();
37244                 delete this.activeItem;
37245                 return;
37246             }
37247             if(t.canActivate){
37248                 this.setActiveItem(t, true);
37249             }
37250             return;
37251             
37252             
37253         }
37254         
37255         t.onClick(e);
37256         this.fireEvent("click", this, t, e);
37257     },
37258
37259     // private
37260     setActiveItem : function(item, autoExpand){
37261         if(item != this.activeItem){
37262             if(this.activeItem){
37263                 this.activeItem.deactivate();
37264             }
37265             this.activeItem = item;
37266             item.activate(autoExpand);
37267         }else if(autoExpand){
37268             item.expandMenu();
37269         }
37270     },
37271
37272     // private
37273     tryActivate : function(start, step){
37274         var items = this.items;
37275         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37276             var item = items.get(i);
37277             if(!item.disabled && item.canActivate){
37278                 this.setActiveItem(item, false);
37279                 return item;
37280             }
37281         }
37282         return false;
37283     },
37284
37285     // private
37286     onMouseOver : function(e){
37287         var t;
37288         if(t = this.findTargetItem(e)){
37289             if(t.canActivate && !t.disabled){
37290                 this.setActiveItem(t, true);
37291             }
37292         }
37293         this.fireEvent("mouseover", this, e, t);
37294     },
37295
37296     // private
37297     onMouseOut : function(e){
37298         var t;
37299         if(t = this.findTargetItem(e)){
37300             if(t == this.activeItem && t.shouldDeactivate(e)){
37301                 this.activeItem.deactivate();
37302                 delete this.activeItem;
37303             }
37304         }
37305         this.fireEvent("mouseout", this, e, t);
37306     },
37307
37308     /**
37309      * Read-only.  Returns true if the menu is currently displayed, else false.
37310      * @type Boolean
37311      */
37312     isVisible : function(){
37313         return this.el && !this.hidden;
37314     },
37315
37316     /**
37317      * Displays this menu relative to another element
37318      * @param {String/HTMLElement/Roo.Element} element The element to align to
37319      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37320      * the element (defaults to this.defaultAlign)
37321      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37322      */
37323     show : function(el, pos, parentMenu){
37324         this.parentMenu = parentMenu;
37325         if(!this.el){
37326             this.render();
37327         }
37328         this.fireEvent("beforeshow", this);
37329         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37330     },
37331
37332     /**
37333      * Displays this menu at a specific xy position
37334      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37335      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37336      */
37337     showAt : function(xy, parentMenu, /* private: */_e){
37338         this.parentMenu = parentMenu;
37339         if(!this.el){
37340             this.render();
37341         }
37342         if(_e !== false){
37343             this.fireEvent("beforeshow", this);
37344             xy = this.el.adjustForConstraints(xy);
37345         }
37346         this.el.setXY(xy);
37347         this.el.show();
37348         this.hidden = false;
37349         this.focus();
37350         this.fireEvent("show", this);
37351     },
37352
37353     focus : function(){
37354         if(!this.hidden){
37355             this.doFocus.defer(50, this);
37356         }
37357     },
37358
37359     doFocus : function(){
37360         if(!this.hidden){
37361             this.focusEl.focus();
37362         }
37363     },
37364
37365     /**
37366      * Hides this menu and optionally all parent menus
37367      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37368      */
37369     hide : function(deep){
37370         if(this.el && this.isVisible()){
37371             this.fireEvent("beforehide", this);
37372             if(this.activeItem){
37373                 this.activeItem.deactivate();
37374                 this.activeItem = null;
37375             }
37376             this.el.hide();
37377             this.hidden = true;
37378             this.fireEvent("hide", this);
37379         }
37380         if(deep === true && this.parentMenu){
37381             this.parentMenu.hide(true);
37382         }
37383     },
37384
37385     /**
37386      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37387      * Any of the following are valid:
37388      * <ul>
37389      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37390      * <li>An HTMLElement object which will be converted to a menu item</li>
37391      * <li>A menu item config object that will be created as a new menu item</li>
37392      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37393      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37394      * </ul>
37395      * Usage:
37396      * <pre><code>
37397 // Create the menu
37398 var menu = new Roo.menu.Menu();
37399
37400 // Create a menu item to add by reference
37401 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37402
37403 // Add a bunch of items at once using different methods.
37404 // Only the last item added will be returned.
37405 var item = menu.add(
37406     menuItem,                // add existing item by ref
37407     'Dynamic Item',          // new TextItem
37408     '-',                     // new separator
37409     { text: 'Config Item' }  // new item by config
37410 );
37411 </code></pre>
37412      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37413      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37414      */
37415     add : function(){
37416         var a = arguments, l = a.length, item;
37417         for(var i = 0; i < l; i++){
37418             var el = a[i];
37419             if ((typeof(el) == "object") && el.xtype && el.xns) {
37420                 el = Roo.factory(el, Roo.menu);
37421             }
37422             
37423             if(el.render){ // some kind of Item
37424                 item = this.addItem(el);
37425             }else if(typeof el == "string"){ // string
37426                 if(el == "separator" || el == "-"){
37427                     item = this.addSeparator();
37428                 }else{
37429                     item = this.addText(el);
37430                 }
37431             }else if(el.tagName || el.el){ // element
37432                 item = this.addElement(el);
37433             }else if(typeof el == "object"){ // must be menu item config?
37434                 item = this.addMenuItem(el);
37435             }
37436         }
37437         return item;
37438     },
37439
37440     /**
37441      * Returns this menu's underlying {@link Roo.Element} object
37442      * @return {Roo.Element} The element
37443      */
37444     getEl : function(){
37445         if(!this.el){
37446             this.render();
37447         }
37448         return this.el;
37449     },
37450
37451     /**
37452      * Adds a separator bar to the menu
37453      * @return {Roo.menu.Item} The menu item that was added
37454      */
37455     addSeparator : function(){
37456         return this.addItem(new Roo.menu.Separator());
37457     },
37458
37459     /**
37460      * Adds an {@link Roo.Element} object to the menu
37461      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37462      * @return {Roo.menu.Item} The menu item that was added
37463      */
37464     addElement : function(el){
37465         return this.addItem(new Roo.menu.BaseItem(el));
37466     },
37467
37468     /**
37469      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37470      * @param {Roo.menu.Item} item The menu item to add
37471      * @return {Roo.menu.Item} The menu item that was added
37472      */
37473     addItem : function(item){
37474         this.items.add(item);
37475         if(this.ul){
37476             var li = document.createElement("li");
37477             li.className = "x-menu-list-item";
37478             this.ul.dom.appendChild(li);
37479             item.render(li, this);
37480             this.delayAutoWidth();
37481         }
37482         return item;
37483     },
37484
37485     /**
37486      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37487      * @param {Object} config A MenuItem config object
37488      * @return {Roo.menu.Item} The menu item that was added
37489      */
37490     addMenuItem : function(config){
37491         if(!(config instanceof Roo.menu.Item)){
37492             if(typeof config.checked == "boolean"){ // must be check menu item config?
37493                 config = new Roo.menu.CheckItem(config);
37494             }else{
37495                 config = new Roo.menu.Item(config);
37496             }
37497         }
37498         return this.addItem(config);
37499     },
37500
37501     /**
37502      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37503      * @param {String} text The text to display in the menu item
37504      * @return {Roo.menu.Item} The menu item that was added
37505      */
37506     addText : function(text){
37507         return this.addItem(new Roo.menu.TextItem({ text : text }));
37508     },
37509
37510     /**
37511      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37512      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37513      * @param {Roo.menu.Item} item The menu item to add
37514      * @return {Roo.menu.Item} The menu item that was added
37515      */
37516     insert : function(index, item){
37517         this.items.insert(index, item);
37518         if(this.ul){
37519             var li = document.createElement("li");
37520             li.className = "x-menu-list-item";
37521             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37522             item.render(li, this);
37523             this.delayAutoWidth();
37524         }
37525         return item;
37526     },
37527
37528     /**
37529      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37530      * @param {Roo.menu.Item} item The menu item to remove
37531      */
37532     remove : function(item){
37533         this.items.removeKey(item.id);
37534         item.destroy();
37535     },
37536
37537     /**
37538      * Removes and destroys all items in the menu
37539      */
37540     removeAll : function(){
37541         var f;
37542         while(f = this.items.first()){
37543             this.remove(f);
37544         }
37545     }
37546 });
37547
37548 // MenuNav is a private utility class used internally by the Menu
37549 Roo.menu.MenuNav = function(menu){
37550     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37551     this.scope = this.menu = menu;
37552 };
37553
37554 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37555     doRelay : function(e, h){
37556         var k = e.getKey();
37557         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37558             this.menu.tryActivate(0, 1);
37559             return false;
37560         }
37561         return h.call(this.scope || this, e, this.menu);
37562     },
37563
37564     up : function(e, m){
37565         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37566             m.tryActivate(m.items.length-1, -1);
37567         }
37568     },
37569
37570     down : function(e, m){
37571         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37572             m.tryActivate(0, 1);
37573         }
37574     },
37575
37576     right : function(e, m){
37577         if(m.activeItem){
37578             m.activeItem.expandMenu(true);
37579         }
37580     },
37581
37582     left : function(e, m){
37583         m.hide();
37584         if(m.parentMenu && m.parentMenu.activeItem){
37585             m.parentMenu.activeItem.activate();
37586         }
37587     },
37588
37589     enter : function(e, m){
37590         if(m.activeItem){
37591             e.stopPropagation();
37592             m.activeItem.onClick(e);
37593             m.fireEvent("click", this, m.activeItem);
37594             return true;
37595         }
37596     }
37597 });/*
37598  * Based on:
37599  * Ext JS Library 1.1.1
37600  * Copyright(c) 2006-2007, Ext JS, LLC.
37601  *
37602  * Originally Released Under LGPL - original licence link has changed is not relivant.
37603  *
37604  * Fork - LGPL
37605  * <script type="text/javascript">
37606  */
37607  
37608 /**
37609  * @class Roo.menu.MenuMgr
37610  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37611  * @singleton
37612  */
37613 Roo.menu.MenuMgr = function(){
37614    var menus, active, groups = {}, attached = false, lastShow = new Date();
37615
37616    // private - called when first menu is created
37617    function init(){
37618        menus = {};
37619        active = new Roo.util.MixedCollection();
37620        Roo.get(document).addKeyListener(27, function(){
37621            if(active.length > 0){
37622                hideAll();
37623            }
37624        });
37625    }
37626
37627    // private
37628    function hideAll(){
37629        if(active && active.length > 0){
37630            var c = active.clone();
37631            c.each(function(m){
37632                m.hide();
37633            });
37634        }
37635    }
37636
37637    // private
37638    function onHide(m){
37639        active.remove(m);
37640        if(active.length < 1){
37641            Roo.get(document).un("mousedown", onMouseDown);
37642            attached = false;
37643        }
37644    }
37645
37646    // private
37647    function onShow(m){
37648        var last = active.last();
37649        lastShow = new Date();
37650        active.add(m);
37651        if(!attached){
37652            Roo.get(document).on("mousedown", onMouseDown);
37653            attached = true;
37654        }
37655        if(m.parentMenu){
37656           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37657           m.parentMenu.activeChild = m;
37658        }else if(last && last.isVisible()){
37659           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37660        }
37661    }
37662
37663    // private
37664    function onBeforeHide(m){
37665        if(m.activeChild){
37666            m.activeChild.hide();
37667        }
37668        if(m.autoHideTimer){
37669            clearTimeout(m.autoHideTimer);
37670            delete m.autoHideTimer;
37671        }
37672    }
37673
37674    // private
37675    function onBeforeShow(m){
37676        var pm = m.parentMenu;
37677        if(!pm && !m.allowOtherMenus){
37678            hideAll();
37679        }else if(pm && pm.activeChild && active != m){
37680            pm.activeChild.hide();
37681        }
37682    }
37683
37684    // private
37685    function onMouseDown(e){
37686        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37687            hideAll();
37688        }
37689    }
37690
37691    // private
37692    function onBeforeCheck(mi, state){
37693        if(state){
37694            var g = groups[mi.group];
37695            for(var i = 0, l = g.length; i < l; i++){
37696                if(g[i] != mi){
37697                    g[i].setChecked(false);
37698                }
37699            }
37700        }
37701    }
37702
37703    return {
37704
37705        /**
37706         * Hides all menus that are currently visible
37707         */
37708        hideAll : function(){
37709             hideAll();  
37710        },
37711
37712        // private
37713        register : function(menu){
37714            if(!menus){
37715                init();
37716            }
37717            menus[menu.id] = menu;
37718            menu.on("beforehide", onBeforeHide);
37719            menu.on("hide", onHide);
37720            menu.on("beforeshow", onBeforeShow);
37721            menu.on("show", onShow);
37722            var g = menu.group;
37723            if(g && menu.events["checkchange"]){
37724                if(!groups[g]){
37725                    groups[g] = [];
37726                }
37727                groups[g].push(menu);
37728                menu.on("checkchange", onCheck);
37729            }
37730        },
37731
37732         /**
37733          * Returns a {@link Roo.menu.Menu} object
37734          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37735          * be used to generate and return a new Menu instance.
37736          */
37737        get : function(menu){
37738            if(typeof menu == "string"){ // menu id
37739                return menus[menu];
37740            }else if(menu.events){  // menu instance
37741                return menu;
37742            }else if(typeof menu.length == 'number'){ // array of menu items?
37743                return new Roo.menu.Menu({items:menu});
37744            }else{ // otherwise, must be a config
37745                return new Roo.menu.Menu(menu);
37746            }
37747        },
37748
37749        // private
37750        unregister : function(menu){
37751            delete menus[menu.id];
37752            menu.un("beforehide", onBeforeHide);
37753            menu.un("hide", onHide);
37754            menu.un("beforeshow", onBeforeShow);
37755            menu.un("show", onShow);
37756            var g = menu.group;
37757            if(g && menu.events["checkchange"]){
37758                groups[g].remove(menu);
37759                menu.un("checkchange", onCheck);
37760            }
37761        },
37762
37763        // private
37764        registerCheckable : function(menuItem){
37765            var g = menuItem.group;
37766            if(g){
37767                if(!groups[g]){
37768                    groups[g] = [];
37769                }
37770                groups[g].push(menuItem);
37771                menuItem.on("beforecheckchange", onBeforeCheck);
37772            }
37773        },
37774
37775        // private
37776        unregisterCheckable : function(menuItem){
37777            var g = menuItem.group;
37778            if(g){
37779                groups[g].remove(menuItem);
37780                menuItem.un("beforecheckchange", onBeforeCheck);
37781            }
37782        }
37783    };
37784 }();/*
37785  * Based on:
37786  * Ext JS Library 1.1.1
37787  * Copyright(c) 2006-2007, Ext JS, LLC.
37788  *
37789  * Originally Released Under LGPL - original licence link has changed is not relivant.
37790  *
37791  * Fork - LGPL
37792  * <script type="text/javascript">
37793  */
37794  
37795
37796 /**
37797  * @class Roo.menu.BaseItem
37798  * @extends Roo.Component
37799  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37800  * management and base configuration options shared by all menu components.
37801  * @constructor
37802  * Creates a new BaseItem
37803  * @param {Object} config Configuration options
37804  */
37805 Roo.menu.BaseItem = function(config){
37806     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37807
37808     this.addEvents({
37809         /**
37810          * @event click
37811          * Fires when this item is clicked
37812          * @param {Roo.menu.BaseItem} this
37813          * @param {Roo.EventObject} e
37814          */
37815         click: true,
37816         /**
37817          * @event activate
37818          * Fires when this item is activated
37819          * @param {Roo.menu.BaseItem} this
37820          */
37821         activate : true,
37822         /**
37823          * @event deactivate
37824          * Fires when this item is deactivated
37825          * @param {Roo.menu.BaseItem} this
37826          */
37827         deactivate : true
37828     });
37829
37830     if(this.handler){
37831         this.on("click", this.handler, this.scope, true);
37832     }
37833 };
37834
37835 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37836     /**
37837      * @cfg {Function} handler
37838      * A function that will handle the click event of this menu item (defaults to undefined)
37839      */
37840     /**
37841      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37842      */
37843     canActivate : false,
37844     
37845      /**
37846      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37847      */
37848     hidden: false,
37849     
37850     /**
37851      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37852      */
37853     activeClass : "x-menu-item-active",
37854     /**
37855      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37856      */
37857     hideOnClick : true,
37858     /**
37859      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37860      */
37861     hideDelay : 100,
37862
37863     // private
37864     ctype: "Roo.menu.BaseItem",
37865
37866     // private
37867     actionMode : "container",
37868
37869     // private
37870     render : function(container, parentMenu){
37871         this.parentMenu = parentMenu;
37872         Roo.menu.BaseItem.superclass.render.call(this, container);
37873         this.container.menuItemId = this.id;
37874     },
37875
37876     // private
37877     onRender : function(container, position){
37878         this.el = Roo.get(this.el);
37879         container.dom.appendChild(this.el.dom);
37880     },
37881
37882     // private
37883     onClick : function(e){
37884         if(!this.disabled && this.fireEvent("click", this, e) !== false
37885                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37886             this.handleClick(e);
37887         }else{
37888             e.stopEvent();
37889         }
37890     },
37891
37892     // private
37893     activate : function(){
37894         if(this.disabled){
37895             return false;
37896         }
37897         var li = this.container;
37898         li.addClass(this.activeClass);
37899         this.region = li.getRegion().adjust(2, 2, -2, -2);
37900         this.fireEvent("activate", this);
37901         return true;
37902     },
37903
37904     // private
37905     deactivate : function(){
37906         this.container.removeClass(this.activeClass);
37907         this.fireEvent("deactivate", this);
37908     },
37909
37910     // private
37911     shouldDeactivate : function(e){
37912         return !this.region || !this.region.contains(e.getPoint());
37913     },
37914
37915     // private
37916     handleClick : function(e){
37917         if(this.hideOnClick){
37918             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37919         }
37920     },
37921
37922     // private
37923     expandMenu : function(autoActivate){
37924         // do nothing
37925     },
37926
37927     // private
37928     hideMenu : function(){
37929         // do nothing
37930     }
37931 });/*
37932  * Based on:
37933  * Ext JS Library 1.1.1
37934  * Copyright(c) 2006-2007, Ext JS, LLC.
37935  *
37936  * Originally Released Under LGPL - original licence link has changed is not relivant.
37937  *
37938  * Fork - LGPL
37939  * <script type="text/javascript">
37940  */
37941  
37942 /**
37943  * @class Roo.menu.Adapter
37944  * @extends Roo.menu.BaseItem
37945  * 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.
37946  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
37947  * @constructor
37948  * Creates a new Adapter
37949  * @param {Object} config Configuration options
37950  */
37951 Roo.menu.Adapter = function(component, config){
37952     Roo.menu.Adapter.superclass.constructor.call(this, config);
37953     this.component = component;
37954 };
37955 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
37956     // private
37957     canActivate : true,
37958
37959     // private
37960     onRender : function(container, position){
37961         this.component.render(container);
37962         this.el = this.component.getEl();
37963     },
37964
37965     // private
37966     activate : function(){
37967         if(this.disabled){
37968             return false;
37969         }
37970         this.component.focus();
37971         this.fireEvent("activate", this);
37972         return true;
37973     },
37974
37975     // private
37976     deactivate : function(){
37977         this.fireEvent("deactivate", this);
37978     },
37979
37980     // private
37981     disable : function(){
37982         this.component.disable();
37983         Roo.menu.Adapter.superclass.disable.call(this);
37984     },
37985
37986     // private
37987     enable : function(){
37988         this.component.enable();
37989         Roo.menu.Adapter.superclass.enable.call(this);
37990     }
37991 });/*
37992  * Based on:
37993  * Ext JS Library 1.1.1
37994  * Copyright(c) 2006-2007, Ext JS, LLC.
37995  *
37996  * Originally Released Under LGPL - original licence link has changed is not relivant.
37997  *
37998  * Fork - LGPL
37999  * <script type="text/javascript">
38000  */
38001
38002 /**
38003  * @class Roo.menu.TextItem
38004  * @extends Roo.menu.BaseItem
38005  * Adds a static text string to a menu, usually used as either a heading or group separator.
38006  * Note: old style constructor with text is still supported.
38007  * 
38008  * @constructor
38009  * Creates a new TextItem
38010  * @param {Object} cfg Configuration
38011  */
38012 Roo.menu.TextItem = function(cfg){
38013     if (typeof(cfg) == 'string') {
38014         this.text = cfg;
38015     } else {
38016         Roo.apply(this,cfg);
38017     }
38018     
38019     Roo.menu.TextItem.superclass.constructor.call(this);
38020 };
38021
38022 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38023     /**
38024      * @cfg {Boolean} text Text to show on item.
38025      */
38026     text : '',
38027     
38028     /**
38029      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38030      */
38031     hideOnClick : false,
38032     /**
38033      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38034      */
38035     itemCls : "x-menu-text",
38036
38037     // private
38038     onRender : function(){
38039         var s = document.createElement("span");
38040         s.className = this.itemCls;
38041         s.innerHTML = this.text;
38042         this.el = s;
38043         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38044     }
38045 });/*
38046  * Based on:
38047  * Ext JS Library 1.1.1
38048  * Copyright(c) 2006-2007, Ext JS, LLC.
38049  *
38050  * Originally Released Under LGPL - original licence link has changed is not relivant.
38051  *
38052  * Fork - LGPL
38053  * <script type="text/javascript">
38054  */
38055
38056 /**
38057  * @class Roo.menu.Separator
38058  * @extends Roo.menu.BaseItem
38059  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38060  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38061  * @constructor
38062  * @param {Object} config Configuration options
38063  */
38064 Roo.menu.Separator = function(config){
38065     Roo.menu.Separator.superclass.constructor.call(this, config);
38066 };
38067
38068 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38069     /**
38070      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38071      */
38072     itemCls : "x-menu-sep",
38073     /**
38074      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38075      */
38076     hideOnClick : false,
38077
38078     // private
38079     onRender : function(li){
38080         var s = document.createElement("span");
38081         s.className = this.itemCls;
38082         s.innerHTML = "&#160;";
38083         this.el = s;
38084         li.addClass("x-menu-sep-li");
38085         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38086     }
38087 });/*
38088  * Based on:
38089  * Ext JS Library 1.1.1
38090  * Copyright(c) 2006-2007, Ext JS, LLC.
38091  *
38092  * Originally Released Under LGPL - original licence link has changed is not relivant.
38093  *
38094  * Fork - LGPL
38095  * <script type="text/javascript">
38096  */
38097 /**
38098  * @class Roo.menu.Item
38099  * @extends Roo.menu.BaseItem
38100  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38101  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38102  * activation and click handling.
38103  * @constructor
38104  * Creates a new Item
38105  * @param {Object} config Configuration options
38106  */
38107 Roo.menu.Item = function(config){
38108     Roo.menu.Item.superclass.constructor.call(this, config);
38109     if(this.menu){
38110         this.menu = Roo.menu.MenuMgr.get(this.menu);
38111     }
38112 };
38113 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38114     
38115     /**
38116      * @cfg {String} text
38117      * The text to show on the menu item.
38118      */
38119     text: '',
38120      /**
38121      * @cfg {String} HTML to render in menu
38122      * The text to show on the menu item (HTML version).
38123      */
38124     html: '',
38125     /**
38126      * @cfg {String} icon
38127      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38128      */
38129     icon: undefined,
38130     /**
38131      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38132      */
38133     itemCls : "x-menu-item",
38134     /**
38135      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38136      */
38137     canActivate : true,
38138     /**
38139      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38140      */
38141     showDelay: 200,
38142     // doc'd in BaseItem
38143     hideDelay: 200,
38144
38145     // private
38146     ctype: "Roo.menu.Item",
38147     
38148     // private
38149     onRender : function(container, position){
38150         var el = document.createElement("a");
38151         el.hideFocus = true;
38152         el.unselectable = "on";
38153         el.href = this.href || "#";
38154         if(this.hrefTarget){
38155             el.target = this.hrefTarget;
38156         }
38157         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38158         
38159         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38160         
38161         el.innerHTML = String.format(
38162                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38163                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38164         this.el = el;
38165         Roo.menu.Item.superclass.onRender.call(this, container, position);
38166     },
38167
38168     /**
38169      * Sets the text to display in this menu item
38170      * @param {String} text The text to display
38171      * @param {Boolean} isHTML true to indicate text is pure html.
38172      */
38173     setText : function(text, isHTML){
38174         if (isHTML) {
38175             this.html = text;
38176         } else {
38177             this.text = text;
38178             this.html = '';
38179         }
38180         if(this.rendered){
38181             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38182      
38183             this.el.update(String.format(
38184                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38185                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38186             this.parentMenu.autoWidth();
38187         }
38188     },
38189
38190     // private
38191     handleClick : function(e){
38192         if(!this.href){ // if no link defined, stop the event automatically
38193             e.stopEvent();
38194         }
38195         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38196     },
38197
38198     // private
38199     activate : function(autoExpand){
38200         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38201             this.focus();
38202             if(autoExpand){
38203                 this.expandMenu();
38204             }
38205         }
38206         return true;
38207     },
38208
38209     // private
38210     shouldDeactivate : function(e){
38211         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38212             if(this.menu && this.menu.isVisible()){
38213                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38214             }
38215             return true;
38216         }
38217         return false;
38218     },
38219
38220     // private
38221     deactivate : function(){
38222         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38223         this.hideMenu();
38224     },
38225
38226     // private
38227     expandMenu : function(autoActivate){
38228         if(!this.disabled && this.menu){
38229             clearTimeout(this.hideTimer);
38230             delete this.hideTimer;
38231             if(!this.menu.isVisible() && !this.showTimer){
38232                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38233             }else if (this.menu.isVisible() && autoActivate){
38234                 this.menu.tryActivate(0, 1);
38235             }
38236         }
38237     },
38238
38239     // private
38240     deferExpand : function(autoActivate){
38241         delete this.showTimer;
38242         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38243         if(autoActivate){
38244             this.menu.tryActivate(0, 1);
38245         }
38246     },
38247
38248     // private
38249     hideMenu : function(){
38250         clearTimeout(this.showTimer);
38251         delete this.showTimer;
38252         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38253             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38254         }
38255     },
38256
38257     // private
38258     deferHide : function(){
38259         delete this.hideTimer;
38260         this.menu.hide();
38261     }
38262 });/*
38263  * Based on:
38264  * Ext JS Library 1.1.1
38265  * Copyright(c) 2006-2007, Ext JS, LLC.
38266  *
38267  * Originally Released Under LGPL - original licence link has changed is not relivant.
38268  *
38269  * Fork - LGPL
38270  * <script type="text/javascript">
38271  */
38272  
38273 /**
38274  * @class Roo.menu.CheckItem
38275  * @extends Roo.menu.Item
38276  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38277  * @constructor
38278  * Creates a new CheckItem
38279  * @param {Object} config Configuration options
38280  */
38281 Roo.menu.CheckItem = function(config){
38282     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38283     this.addEvents({
38284         /**
38285          * @event beforecheckchange
38286          * Fires before the checked value is set, providing an opportunity to cancel if needed
38287          * @param {Roo.menu.CheckItem} this
38288          * @param {Boolean} checked The new checked value that will be set
38289          */
38290         "beforecheckchange" : true,
38291         /**
38292          * @event checkchange
38293          * Fires after the checked value has been set
38294          * @param {Roo.menu.CheckItem} this
38295          * @param {Boolean} checked The checked value that was set
38296          */
38297         "checkchange" : true
38298     });
38299     if(this.checkHandler){
38300         this.on('checkchange', this.checkHandler, this.scope);
38301     }
38302 };
38303 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38304     /**
38305      * @cfg {String} group
38306      * All check items with the same group name will automatically be grouped into a single-select
38307      * radio button group (defaults to '')
38308      */
38309     /**
38310      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38311      */
38312     itemCls : "x-menu-item x-menu-check-item",
38313     /**
38314      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38315      */
38316     groupClass : "x-menu-group-item",
38317
38318     /**
38319      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38320      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38321      * initialized with checked = true will be rendered as checked.
38322      */
38323     checked: false,
38324
38325     // private
38326     ctype: "Roo.menu.CheckItem",
38327
38328     // private
38329     onRender : function(c){
38330         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38331         if(this.group){
38332             this.el.addClass(this.groupClass);
38333         }
38334         Roo.menu.MenuMgr.registerCheckable(this);
38335         if(this.checked){
38336             this.checked = false;
38337             this.setChecked(true, true);
38338         }
38339     },
38340
38341     // private
38342     destroy : function(){
38343         if(this.rendered){
38344             Roo.menu.MenuMgr.unregisterCheckable(this);
38345         }
38346         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38347     },
38348
38349     /**
38350      * Set the checked state of this item
38351      * @param {Boolean} checked The new checked value
38352      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38353      */
38354     setChecked : function(state, suppressEvent){
38355         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38356             if(this.container){
38357                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38358             }
38359             this.checked = state;
38360             if(suppressEvent !== true){
38361                 this.fireEvent("checkchange", this, state);
38362             }
38363         }
38364     },
38365
38366     // private
38367     handleClick : function(e){
38368        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38369            this.setChecked(!this.checked);
38370        }
38371        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38372     }
38373 });/*
38374  * Based on:
38375  * Ext JS Library 1.1.1
38376  * Copyright(c) 2006-2007, Ext JS, LLC.
38377  *
38378  * Originally Released Under LGPL - original licence link has changed is not relivant.
38379  *
38380  * Fork - LGPL
38381  * <script type="text/javascript">
38382  */
38383  
38384 /**
38385  * @class Roo.menu.DateItem
38386  * @extends Roo.menu.Adapter
38387  * A menu item that wraps the {@link Roo.DatPicker} component.
38388  * @constructor
38389  * Creates a new DateItem
38390  * @param {Object} config Configuration options
38391  */
38392 Roo.menu.DateItem = function(config){
38393     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38394     /** The Roo.DatePicker object @type Roo.DatePicker */
38395     this.picker = this.component;
38396     this.addEvents({select: true});
38397     
38398     this.picker.on("render", function(picker){
38399         picker.getEl().swallowEvent("click");
38400         picker.container.addClass("x-menu-date-item");
38401     });
38402
38403     this.picker.on("select", this.onSelect, this);
38404 };
38405
38406 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38407     // private
38408     onSelect : function(picker, date){
38409         this.fireEvent("select", this, date, picker);
38410         Roo.menu.DateItem.superclass.handleClick.call(this);
38411     }
38412 });/*
38413  * Based on:
38414  * Ext JS Library 1.1.1
38415  * Copyright(c) 2006-2007, Ext JS, LLC.
38416  *
38417  * Originally Released Under LGPL - original licence link has changed is not relivant.
38418  *
38419  * Fork - LGPL
38420  * <script type="text/javascript">
38421  */
38422  
38423 /**
38424  * @class Roo.menu.ColorItem
38425  * @extends Roo.menu.Adapter
38426  * A menu item that wraps the {@link Roo.ColorPalette} component.
38427  * @constructor
38428  * Creates a new ColorItem
38429  * @param {Object} config Configuration options
38430  */
38431 Roo.menu.ColorItem = function(config){
38432     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38433     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38434     this.palette = this.component;
38435     this.relayEvents(this.palette, ["select"]);
38436     if(this.selectHandler){
38437         this.on('select', this.selectHandler, this.scope);
38438     }
38439 };
38440 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38441  * Based on:
38442  * Ext JS Library 1.1.1
38443  * Copyright(c) 2006-2007, Ext JS, LLC.
38444  *
38445  * Originally Released Under LGPL - original licence link has changed is not relivant.
38446  *
38447  * Fork - LGPL
38448  * <script type="text/javascript">
38449  */
38450  
38451
38452 /**
38453  * @class Roo.menu.DateMenu
38454  * @extends Roo.menu.Menu
38455  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38456  * @constructor
38457  * Creates a new DateMenu
38458  * @param {Object} config Configuration options
38459  */
38460 Roo.menu.DateMenu = function(config){
38461     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38462     this.plain = true;
38463     var di = new Roo.menu.DateItem(config);
38464     this.add(di);
38465     /**
38466      * The {@link Roo.DatePicker} instance for this DateMenu
38467      * @type DatePicker
38468      */
38469     this.picker = di.picker;
38470     /**
38471      * @event select
38472      * @param {DatePicker} picker
38473      * @param {Date} date
38474      */
38475     this.relayEvents(di, ["select"]);
38476     this.on('beforeshow', function(){
38477         if(this.picker){
38478             this.picker.hideMonthPicker(false);
38479         }
38480     }, this);
38481 };
38482 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38483     cls:'x-date-menu'
38484 });/*
38485  * Based on:
38486  * Ext JS Library 1.1.1
38487  * Copyright(c) 2006-2007, Ext JS, LLC.
38488  *
38489  * Originally Released Under LGPL - original licence link has changed is not relivant.
38490  *
38491  * Fork - LGPL
38492  * <script type="text/javascript">
38493  */
38494  
38495
38496 /**
38497  * @class Roo.menu.ColorMenu
38498  * @extends Roo.menu.Menu
38499  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38500  * @constructor
38501  * Creates a new ColorMenu
38502  * @param {Object} config Configuration options
38503  */
38504 Roo.menu.ColorMenu = function(config){
38505     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38506     this.plain = true;
38507     var ci = new Roo.menu.ColorItem(config);
38508     this.add(ci);
38509     /**
38510      * The {@link Roo.ColorPalette} instance for this ColorMenu
38511      * @type ColorPalette
38512      */
38513     this.palette = ci.palette;
38514     /**
38515      * @event select
38516      * @param {ColorPalette} palette
38517      * @param {String} color
38518      */
38519     this.relayEvents(ci, ["select"]);
38520 };
38521 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38522  * Based on:
38523  * Ext JS Library 1.1.1
38524  * Copyright(c) 2006-2007, Ext JS, LLC.
38525  *
38526  * Originally Released Under LGPL - original licence link has changed is not relivant.
38527  *
38528  * Fork - LGPL
38529  * <script type="text/javascript">
38530  */
38531  
38532 /**
38533  * @class Roo.form.Field
38534  * @extends Roo.BoxComponent
38535  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38536  * @constructor
38537  * Creates a new Field
38538  * @param {Object} config Configuration options
38539  */
38540 Roo.form.Field = function(config){
38541     Roo.form.Field.superclass.constructor.call(this, config);
38542 };
38543
38544 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38545     /**
38546      * @cfg {String} fieldLabel Label to use when rendering a form.
38547      */
38548        /**
38549      * @cfg {String} qtip Mouse over tip
38550      */
38551      
38552     /**
38553      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38554      */
38555     invalidClass : "x-form-invalid",
38556     /**
38557      * @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")
38558      */
38559     invalidText : "The value in this field is invalid",
38560     /**
38561      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38562      */
38563     focusClass : "x-form-focus",
38564     /**
38565      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38566       automatic validation (defaults to "keyup").
38567      */
38568     validationEvent : "keyup",
38569     /**
38570      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38571      */
38572     validateOnBlur : true,
38573     /**
38574      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38575      */
38576     validationDelay : 250,
38577     /**
38578      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38579      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38580      */
38581     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38582     /**
38583      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38584      */
38585     fieldClass : "x-form-field",
38586     /**
38587      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38588      *<pre>
38589 Value         Description
38590 -----------   ----------------------------------------------------------------------
38591 qtip          Display a quick tip when the user hovers over the field
38592 title         Display a default browser title attribute popup
38593 under         Add a block div beneath the field containing the error text
38594 side          Add an error icon to the right of the field with a popup on hover
38595 [element id]  Add the error text directly to the innerHTML of the specified element
38596 </pre>
38597      */
38598     msgTarget : 'qtip',
38599     /**
38600      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38601      */
38602     msgFx : 'normal',
38603
38604     /**
38605      * @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.
38606      */
38607     readOnly : false,
38608
38609     /**
38610      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38611      */
38612     disabled : false,
38613
38614     /**
38615      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38616      */
38617     inputType : undefined,
38618     
38619     /**
38620      * @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).
38621          */
38622         tabIndex : undefined,
38623         
38624     // private
38625     isFormField : true,
38626
38627     // private
38628     hasFocus : false,
38629     /**
38630      * @property {Roo.Element} fieldEl
38631      * Element Containing the rendered Field (with label etc.)
38632      */
38633     /**
38634      * @cfg {Mixed} value A value to initialize this field with.
38635      */
38636     value : undefined,
38637
38638     /**
38639      * @cfg {String} name The field's HTML name attribute.
38640      */
38641     /**
38642      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38643      */
38644     // private
38645     loadedValue : false,
38646      
38647      
38648         // private ??
38649         initComponent : function(){
38650         Roo.form.Field.superclass.initComponent.call(this);
38651         this.addEvents({
38652             /**
38653              * @event focus
38654              * Fires when this field receives input focus.
38655              * @param {Roo.form.Field} this
38656              */
38657             focus : true,
38658             /**
38659              * @event blur
38660              * Fires when this field loses input focus.
38661              * @param {Roo.form.Field} this
38662              */
38663             blur : true,
38664             /**
38665              * @event specialkey
38666              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38667              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38668              * @param {Roo.form.Field} this
38669              * @param {Roo.EventObject} e The event object
38670              */
38671             specialkey : true,
38672             /**
38673              * @event change
38674              * Fires just before the field blurs if the field value has changed.
38675              * @param {Roo.form.Field} this
38676              * @param {Mixed} newValue The new value
38677              * @param {Mixed} oldValue The original value
38678              */
38679             change : true,
38680             /**
38681              * @event invalid
38682              * Fires after the field has been marked as invalid.
38683              * @param {Roo.form.Field} this
38684              * @param {String} msg The validation message
38685              */
38686             invalid : true,
38687             /**
38688              * @event valid
38689              * Fires after the field has been validated with no errors.
38690              * @param {Roo.form.Field} this
38691              */
38692             valid : true,
38693              /**
38694              * @event keyup
38695              * Fires after the key up
38696              * @param {Roo.form.Field} this
38697              * @param {Roo.EventObject}  e The event Object
38698              */
38699             keyup : true
38700         });
38701     },
38702
38703     /**
38704      * Returns the name attribute of the field if available
38705      * @return {String} name The field name
38706      */
38707     getName: function(){
38708          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38709     },
38710
38711     // private
38712     onRender : function(ct, position){
38713         Roo.form.Field.superclass.onRender.call(this, ct, position);
38714         if(!this.el){
38715             var cfg = this.getAutoCreate();
38716             if(!cfg.name){
38717                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38718             }
38719             if (!cfg.name.length) {
38720                 delete cfg.name;
38721             }
38722             if(this.inputType){
38723                 cfg.type = this.inputType;
38724             }
38725             this.el = ct.createChild(cfg, position);
38726         }
38727         var type = this.el.dom.type;
38728         if(type){
38729             if(type == 'password'){
38730                 type = 'text';
38731             }
38732             this.el.addClass('x-form-'+type);
38733         }
38734         if(this.readOnly){
38735             this.el.dom.readOnly = true;
38736         }
38737         if(this.tabIndex !== undefined){
38738             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38739         }
38740
38741         this.el.addClass([this.fieldClass, this.cls]);
38742         this.initValue();
38743     },
38744
38745     /**
38746      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38747      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38748      * @return {Roo.form.Field} this
38749      */
38750     applyTo : function(target){
38751         this.allowDomMove = false;
38752         this.el = Roo.get(target);
38753         this.render(this.el.dom.parentNode);
38754         return this;
38755     },
38756
38757     // private
38758     initValue : function(){
38759         if(this.value !== undefined){
38760             this.setValue(this.value);
38761         }else if(this.el.dom.value.length > 0){
38762             this.setValue(this.el.dom.value);
38763         }
38764     },
38765
38766     /**
38767      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38768      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38769      */
38770     isDirty : function() {
38771         if(this.disabled) {
38772             return false;
38773         }
38774         return String(this.getValue()) !== String(this.originalValue);
38775     },
38776
38777     /**
38778      * stores the current value in loadedValue
38779      */
38780     resetHasChanged : function()
38781     {
38782         this.loadedValue = String(this.getValue());
38783     },
38784     /**
38785      * checks the current value against the 'loaded' value.
38786      * Note - will return false if 'resetHasChanged' has not been called first.
38787      */
38788     hasChanged : function()
38789     {
38790         if(this.disabled || this.readOnly) {
38791             return false;
38792         }
38793         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38794     },
38795     
38796     
38797     
38798     // private
38799     afterRender : function(){
38800         Roo.form.Field.superclass.afterRender.call(this);
38801         this.initEvents();
38802     },
38803
38804     // private
38805     fireKey : function(e){
38806         //Roo.log('field ' + e.getKey());
38807         if(e.isNavKeyPress()){
38808             this.fireEvent("specialkey", this, e);
38809         }
38810     },
38811
38812     /**
38813      * Resets the current field value to the originally loaded value and clears any validation messages
38814      */
38815     reset : function(){
38816         this.setValue(this.resetValue);
38817         this.clearInvalid();
38818     },
38819
38820     // private
38821     initEvents : function(){
38822         // safari killled keypress - so keydown is now used..
38823         this.el.on("keydown" , this.fireKey,  this);
38824         this.el.on("focus", this.onFocus,  this);
38825         this.el.on("blur", this.onBlur,  this);
38826         this.el.relayEvent('keyup', this);
38827
38828         // reference to original value for reset
38829         this.originalValue = this.getValue();
38830         this.resetValue =  this.getValue();
38831     },
38832
38833     // private
38834     onFocus : function(){
38835         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38836             this.el.addClass(this.focusClass);
38837         }
38838         if(!this.hasFocus){
38839             this.hasFocus = true;
38840             this.startValue = this.getValue();
38841             this.fireEvent("focus", this);
38842         }
38843     },
38844
38845     beforeBlur : Roo.emptyFn,
38846
38847     // private
38848     onBlur : function(){
38849         this.beforeBlur();
38850         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38851             this.el.removeClass(this.focusClass);
38852         }
38853         this.hasFocus = false;
38854         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38855             this.validate();
38856         }
38857         var v = this.getValue();
38858         if(String(v) !== String(this.startValue)){
38859             this.fireEvent('change', this, v, this.startValue);
38860         }
38861         this.fireEvent("blur", this);
38862     },
38863
38864     /**
38865      * Returns whether or not the field value is currently valid
38866      * @param {Boolean} preventMark True to disable marking the field invalid
38867      * @return {Boolean} True if the value is valid, else false
38868      */
38869     isValid : function(preventMark){
38870         if(this.disabled){
38871             return true;
38872         }
38873         var restore = this.preventMark;
38874         this.preventMark = preventMark === true;
38875         var v = this.validateValue(this.processValue(this.getRawValue()));
38876         this.preventMark = restore;
38877         return v;
38878     },
38879
38880     /**
38881      * Validates the field value
38882      * @return {Boolean} True if the value is valid, else false
38883      */
38884     validate : function(){
38885         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38886             this.clearInvalid();
38887             return true;
38888         }
38889         return false;
38890     },
38891
38892     processValue : function(value){
38893         return value;
38894     },
38895
38896     // private
38897     // Subclasses should provide the validation implementation by overriding this
38898     validateValue : function(value){
38899         return true;
38900     },
38901
38902     /**
38903      * Mark this field as invalid
38904      * @param {String} msg The validation message
38905      */
38906     markInvalid : function(msg){
38907         if(!this.rendered || this.preventMark){ // not rendered
38908             return;
38909         }
38910         
38911         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38912         
38913         obj.el.addClass(this.invalidClass);
38914         msg = msg || this.invalidText;
38915         switch(this.msgTarget){
38916             case 'qtip':
38917                 obj.el.dom.qtip = msg;
38918                 obj.el.dom.qclass = 'x-form-invalid-tip';
38919                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38920                     Roo.QuickTips.enable();
38921                 }
38922                 break;
38923             case 'title':
38924                 this.el.dom.title = msg;
38925                 break;
38926             case 'under':
38927                 if(!this.errorEl){
38928                     var elp = this.el.findParent('.x-form-element', 5, true);
38929                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38930                     this.errorEl.setWidth(elp.getWidth(true)-20);
38931                 }
38932                 this.errorEl.update(msg);
38933                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38934                 break;
38935             case 'side':
38936                 if(!this.errorIcon){
38937                     var elp = this.el.findParent('.x-form-element', 5, true);
38938                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38939                 }
38940                 this.alignErrorIcon();
38941                 this.errorIcon.dom.qtip = msg;
38942                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38943                 this.errorIcon.show();
38944                 this.on('resize', this.alignErrorIcon, this);
38945                 break;
38946             default:
38947                 var t = Roo.getDom(this.msgTarget);
38948                 t.innerHTML = msg;
38949                 t.style.display = this.msgDisplay;
38950                 break;
38951         }
38952         this.fireEvent('invalid', this, msg);
38953     },
38954
38955     // private
38956     alignErrorIcon : function(){
38957         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
38958     },
38959
38960     /**
38961      * Clear any invalid styles/messages for this field
38962      */
38963     clearInvalid : function(){
38964         if(!this.rendered || this.preventMark){ // not rendered
38965             return;
38966         }
38967         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38968         
38969         obj.el.removeClass(this.invalidClass);
38970         switch(this.msgTarget){
38971             case 'qtip':
38972                 obj.el.dom.qtip = '';
38973                 break;
38974             case 'title':
38975                 this.el.dom.title = '';
38976                 break;
38977             case 'under':
38978                 if(this.errorEl){
38979                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
38980                 }
38981                 break;
38982             case 'side':
38983                 if(this.errorIcon){
38984                     this.errorIcon.dom.qtip = '';
38985                     this.errorIcon.hide();
38986                     this.un('resize', this.alignErrorIcon, this);
38987                 }
38988                 break;
38989             default:
38990                 var t = Roo.getDom(this.msgTarget);
38991                 t.innerHTML = '';
38992                 t.style.display = 'none';
38993                 break;
38994         }
38995         this.fireEvent('valid', this);
38996     },
38997
38998     /**
38999      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39000      * @return {Mixed} value The field value
39001      */
39002     getRawValue : function(){
39003         var v = this.el.getValue();
39004         
39005         return v;
39006     },
39007
39008     /**
39009      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39010      * @return {Mixed} value The field value
39011      */
39012     getValue : function(){
39013         var v = this.el.getValue();
39014          
39015         return v;
39016     },
39017
39018     /**
39019      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39020      * @param {Mixed} value The value to set
39021      */
39022     setRawValue : function(v){
39023         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39024     },
39025
39026     /**
39027      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39028      * @param {Mixed} value The value to set
39029      */
39030     setValue : function(v){
39031         this.value = v;
39032         if(this.rendered){
39033             this.el.dom.value = (v === null || v === undefined ? '' : v);
39034              this.validate();
39035         }
39036     },
39037
39038     adjustSize : function(w, h){
39039         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39040         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39041         return s;
39042     },
39043
39044     adjustWidth : function(tag, w){
39045         tag = tag.toLowerCase();
39046         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39047             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39048                 if(tag == 'input'){
39049                     return w + 2;
39050                 }
39051                 if(tag == 'textarea'){
39052                     return w-2;
39053                 }
39054             }else if(Roo.isOpera){
39055                 if(tag == 'input'){
39056                     return w + 2;
39057                 }
39058                 if(tag == 'textarea'){
39059                     return w-2;
39060                 }
39061             }
39062         }
39063         return w;
39064     }
39065 });
39066
39067
39068 // anything other than normal should be considered experimental
39069 Roo.form.Field.msgFx = {
39070     normal : {
39071         show: function(msgEl, f){
39072             msgEl.setDisplayed('block');
39073         },
39074
39075         hide : function(msgEl, f){
39076             msgEl.setDisplayed(false).update('');
39077         }
39078     },
39079
39080     slide : {
39081         show: function(msgEl, f){
39082             msgEl.slideIn('t', {stopFx:true});
39083         },
39084
39085         hide : function(msgEl, f){
39086             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39087         }
39088     },
39089
39090     slideRight : {
39091         show: function(msgEl, f){
39092             msgEl.fixDisplay();
39093             msgEl.alignTo(f.el, 'tl-tr');
39094             msgEl.slideIn('l', {stopFx:true});
39095         },
39096
39097         hide : function(msgEl, f){
39098             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39099         }
39100     }
39101 };/*
39102  * Based on:
39103  * Ext JS Library 1.1.1
39104  * Copyright(c) 2006-2007, Ext JS, LLC.
39105  *
39106  * Originally Released Under LGPL - original licence link has changed is not relivant.
39107  *
39108  * Fork - LGPL
39109  * <script type="text/javascript">
39110  */
39111  
39112
39113 /**
39114  * @class Roo.form.TextField
39115  * @extends Roo.form.Field
39116  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39117  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39118  * @constructor
39119  * Creates a new TextField
39120  * @param {Object} config Configuration options
39121  */
39122 Roo.form.TextField = function(config){
39123     Roo.form.TextField.superclass.constructor.call(this, config);
39124     this.addEvents({
39125         /**
39126          * @event autosize
39127          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39128          * according to the default logic, but this event provides a hook for the developer to apply additional
39129          * logic at runtime to resize the field if needed.
39130              * @param {Roo.form.Field} this This text field
39131              * @param {Number} width The new field width
39132              */
39133         autosize : true
39134     });
39135 };
39136
39137 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39138     /**
39139      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39140      */
39141     grow : false,
39142     /**
39143      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39144      */
39145     growMin : 30,
39146     /**
39147      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39148      */
39149     growMax : 800,
39150     /**
39151      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39152      */
39153     vtype : null,
39154     /**
39155      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39156      */
39157     maskRe : null,
39158     /**
39159      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39160      */
39161     disableKeyFilter : false,
39162     /**
39163      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39164      */
39165     allowBlank : true,
39166     /**
39167      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39168      */
39169     minLength : 0,
39170     /**
39171      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39172      */
39173     maxLength : Number.MAX_VALUE,
39174     /**
39175      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39176      */
39177     minLengthText : "The minimum length for this field is {0}",
39178     /**
39179      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39180      */
39181     maxLengthText : "The maximum length for this field is {0}",
39182     /**
39183      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39184      */
39185     selectOnFocus : false,
39186     /**
39187      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39188      */
39189     blankText : "This field is required",
39190     /**
39191      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39192      * If available, this function will be called only after the basic validators all return true, and will be passed the
39193      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39194      */
39195     validator : null,
39196     /**
39197      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39198      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39199      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39200      */
39201     regex : null,
39202     /**
39203      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39204      */
39205     regexText : "",
39206     /**
39207      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39208      */
39209     emptyText : null,
39210    
39211
39212     // private
39213     initEvents : function()
39214     {
39215         if (this.emptyText) {
39216             this.el.attr('placeholder', this.emptyText);
39217         }
39218         
39219         Roo.form.TextField.superclass.initEvents.call(this);
39220         if(this.validationEvent == 'keyup'){
39221             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39222             this.el.on('keyup', this.filterValidation, this);
39223         }
39224         else if(this.validationEvent !== false){
39225             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39226         }
39227         
39228         if(this.selectOnFocus){
39229             this.on("focus", this.preFocus, this);
39230             
39231         }
39232         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39233             this.el.on("keypress", this.filterKeys, this);
39234         }
39235         if(this.grow){
39236             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39237             this.el.on("click", this.autoSize,  this);
39238         }
39239         if(this.el.is('input[type=password]') && Roo.isSafari){
39240             this.el.on('keydown', this.SafariOnKeyDown, this);
39241         }
39242     },
39243
39244     processValue : function(value){
39245         if(this.stripCharsRe){
39246             var newValue = value.replace(this.stripCharsRe, '');
39247             if(newValue !== value){
39248                 this.setRawValue(newValue);
39249                 return newValue;
39250             }
39251         }
39252         return value;
39253     },
39254
39255     filterValidation : function(e){
39256         if(!e.isNavKeyPress()){
39257             this.validationTask.delay(this.validationDelay);
39258         }
39259     },
39260
39261     // private
39262     onKeyUp : function(e){
39263         if(!e.isNavKeyPress()){
39264             this.autoSize();
39265         }
39266     },
39267
39268     /**
39269      * Resets the current field value to the originally-loaded value and clears any validation messages.
39270      *  
39271      */
39272     reset : function(){
39273         Roo.form.TextField.superclass.reset.call(this);
39274        
39275     },
39276
39277     
39278     // private
39279     preFocus : function(){
39280         
39281         if(this.selectOnFocus){
39282             this.el.dom.select();
39283         }
39284     },
39285
39286     
39287     // private
39288     filterKeys : function(e){
39289         var k = e.getKey();
39290         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39291             return;
39292         }
39293         var c = e.getCharCode(), cc = String.fromCharCode(c);
39294         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39295             return;
39296         }
39297         if(!this.maskRe.test(cc)){
39298             e.stopEvent();
39299         }
39300     },
39301
39302     setValue : function(v){
39303         
39304         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39305         
39306         this.autoSize();
39307     },
39308
39309     /**
39310      * Validates a value according to the field's validation rules and marks the field as invalid
39311      * if the validation fails
39312      * @param {Mixed} value The value to validate
39313      * @return {Boolean} True if the value is valid, else false
39314      */
39315     validateValue : function(value){
39316         if(value.length < 1)  { // if it's blank
39317              if(this.allowBlank){
39318                 this.clearInvalid();
39319                 return true;
39320              }else{
39321                 this.markInvalid(this.blankText);
39322                 return false;
39323              }
39324         }
39325         if(value.length < this.minLength){
39326             this.markInvalid(String.format(this.minLengthText, this.minLength));
39327             return false;
39328         }
39329         if(value.length > this.maxLength){
39330             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39331             return false;
39332         }
39333         if(this.vtype){
39334             var vt = Roo.form.VTypes;
39335             if(!vt[this.vtype](value, this)){
39336                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39337                 return false;
39338             }
39339         }
39340         if(typeof this.validator == "function"){
39341             var msg = this.validator(value);
39342             if(msg !== true){
39343                 this.markInvalid(msg);
39344                 return false;
39345             }
39346         }
39347         if(this.regex && !this.regex.test(value)){
39348             this.markInvalid(this.regexText);
39349             return false;
39350         }
39351         return true;
39352     },
39353
39354     /**
39355      * Selects text in this field
39356      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39357      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39358      */
39359     selectText : function(start, end){
39360         var v = this.getRawValue();
39361         if(v.length > 0){
39362             start = start === undefined ? 0 : start;
39363             end = end === undefined ? v.length : end;
39364             var d = this.el.dom;
39365             if(d.setSelectionRange){
39366                 d.setSelectionRange(start, end);
39367             }else if(d.createTextRange){
39368                 var range = d.createTextRange();
39369                 range.moveStart("character", start);
39370                 range.moveEnd("character", v.length-end);
39371                 range.select();
39372             }
39373         }
39374     },
39375
39376     /**
39377      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39378      * This only takes effect if grow = true, and fires the autosize event.
39379      */
39380     autoSize : function(){
39381         if(!this.grow || !this.rendered){
39382             return;
39383         }
39384         if(!this.metrics){
39385             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39386         }
39387         var el = this.el;
39388         var v = el.dom.value;
39389         var d = document.createElement('div');
39390         d.appendChild(document.createTextNode(v));
39391         v = d.innerHTML;
39392         d = null;
39393         v += "&#160;";
39394         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39395         this.el.setWidth(w);
39396         this.fireEvent("autosize", this, w);
39397     },
39398     
39399     // private
39400     SafariOnKeyDown : function(event)
39401     {
39402         // this is a workaround for a password hang bug on chrome/ webkit.
39403         
39404         var isSelectAll = false;
39405         
39406         if(this.el.dom.selectionEnd > 0){
39407             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39408         }
39409         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39410             event.preventDefault();
39411             this.setValue('');
39412             return;
39413         }
39414         
39415         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39416             
39417             event.preventDefault();
39418             // this is very hacky as keydown always get's upper case.
39419             
39420             var cc = String.fromCharCode(event.getCharCode());
39421             
39422             
39423             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39424             
39425         }
39426         
39427         
39428     }
39429 });/*
39430  * Based on:
39431  * Ext JS Library 1.1.1
39432  * Copyright(c) 2006-2007, Ext JS, LLC.
39433  *
39434  * Originally Released Under LGPL - original licence link has changed is not relivant.
39435  *
39436  * Fork - LGPL
39437  * <script type="text/javascript">
39438  */
39439  
39440 /**
39441  * @class Roo.form.Hidden
39442  * @extends Roo.form.TextField
39443  * Simple Hidden element used on forms 
39444  * 
39445  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39446  * 
39447  * @constructor
39448  * Creates a new Hidden form element.
39449  * @param {Object} config Configuration options
39450  */
39451
39452
39453
39454 // easy hidden field...
39455 Roo.form.Hidden = function(config){
39456     Roo.form.Hidden.superclass.constructor.call(this, config);
39457 };
39458   
39459 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39460     fieldLabel:      '',
39461     inputType:      'hidden',
39462     width:          50,
39463     allowBlank:     true,
39464     labelSeparator: '',
39465     hidden:         true,
39466     itemCls :       'x-form-item-display-none'
39467
39468
39469 });
39470
39471
39472 /*
39473  * Based on:
39474  * Ext JS Library 1.1.1
39475  * Copyright(c) 2006-2007, Ext JS, LLC.
39476  *
39477  * Originally Released Under LGPL - original licence link has changed is not relivant.
39478  *
39479  * Fork - LGPL
39480  * <script type="text/javascript">
39481  */
39482  
39483 /**
39484  * @class Roo.form.TriggerField
39485  * @extends Roo.form.TextField
39486  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39487  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39488  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39489  * for which you can provide a custom implementation.  For example:
39490  * <pre><code>
39491 var trigger = new Roo.form.TriggerField();
39492 trigger.onTriggerClick = myTriggerFn;
39493 trigger.applyTo('my-field');
39494 </code></pre>
39495  *
39496  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39497  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39498  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39499  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39500  * @constructor
39501  * Create a new TriggerField.
39502  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39503  * to the base TextField)
39504  */
39505 Roo.form.TriggerField = function(config){
39506     this.mimicing = false;
39507     Roo.form.TriggerField.superclass.constructor.call(this, config);
39508 };
39509
39510 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39511     /**
39512      * @cfg {String} triggerClass A CSS class to apply to the trigger
39513      */
39514     /**
39515      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39516      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39517      */
39518     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39519     /**
39520      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39521      */
39522     hideTrigger:false,
39523
39524     /** @cfg {Boolean} grow @hide */
39525     /** @cfg {Number} growMin @hide */
39526     /** @cfg {Number} growMax @hide */
39527
39528     /**
39529      * @hide 
39530      * @method
39531      */
39532     autoSize: Roo.emptyFn,
39533     // private
39534     monitorTab : true,
39535     // private
39536     deferHeight : true,
39537
39538     
39539     actionMode : 'wrap',
39540     // private
39541     onResize : function(w, h){
39542         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39543         if(typeof w == 'number'){
39544             var x = w - this.trigger.getWidth();
39545             this.el.setWidth(this.adjustWidth('input', x));
39546             this.trigger.setStyle('left', x+'px');
39547         }
39548     },
39549
39550     // private
39551     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39552
39553     // private
39554     getResizeEl : function(){
39555         return this.wrap;
39556     },
39557
39558     // private
39559     getPositionEl : function(){
39560         return this.wrap;
39561     },
39562
39563     // private
39564     alignErrorIcon : function(){
39565         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39566     },
39567
39568     // private
39569     onRender : function(ct, position){
39570         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39571         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39572         this.trigger = this.wrap.createChild(this.triggerConfig ||
39573                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39574         if(this.hideTrigger){
39575             this.trigger.setDisplayed(false);
39576         }
39577         this.initTrigger();
39578         if(!this.width){
39579             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39580         }
39581     },
39582
39583     // private
39584     initTrigger : function(){
39585         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39586         this.trigger.addClassOnOver('x-form-trigger-over');
39587         this.trigger.addClassOnClick('x-form-trigger-click');
39588     },
39589
39590     // private
39591     onDestroy : function(){
39592         if(this.trigger){
39593             this.trigger.removeAllListeners();
39594             this.trigger.remove();
39595         }
39596         if(this.wrap){
39597             this.wrap.remove();
39598         }
39599         Roo.form.TriggerField.superclass.onDestroy.call(this);
39600     },
39601
39602     // private
39603     onFocus : function(){
39604         Roo.form.TriggerField.superclass.onFocus.call(this);
39605         if(!this.mimicing){
39606             this.wrap.addClass('x-trigger-wrap-focus');
39607             this.mimicing = true;
39608             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39609             if(this.monitorTab){
39610                 this.el.on("keydown", this.checkTab, this);
39611             }
39612         }
39613     },
39614
39615     // private
39616     checkTab : function(e){
39617         if(e.getKey() == e.TAB){
39618             this.triggerBlur();
39619         }
39620     },
39621
39622     // private
39623     onBlur : function(){
39624         // do nothing
39625     },
39626
39627     // private
39628     mimicBlur : function(e, t){
39629         if(!this.wrap.contains(t) && this.validateBlur()){
39630             this.triggerBlur();
39631         }
39632     },
39633
39634     // private
39635     triggerBlur : function(){
39636         this.mimicing = false;
39637         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39638         if(this.monitorTab){
39639             this.el.un("keydown", this.checkTab, this);
39640         }
39641         this.wrap.removeClass('x-trigger-wrap-focus');
39642         Roo.form.TriggerField.superclass.onBlur.call(this);
39643     },
39644
39645     // private
39646     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39647     validateBlur : function(e, t){
39648         return true;
39649     },
39650
39651     // private
39652     onDisable : function(){
39653         Roo.form.TriggerField.superclass.onDisable.call(this);
39654         if(this.wrap){
39655             this.wrap.addClass('x-item-disabled');
39656         }
39657     },
39658
39659     // private
39660     onEnable : function(){
39661         Roo.form.TriggerField.superclass.onEnable.call(this);
39662         if(this.wrap){
39663             this.wrap.removeClass('x-item-disabled');
39664         }
39665     },
39666
39667     // private
39668     onShow : function(){
39669         var ae = this.getActionEl();
39670         
39671         if(ae){
39672             ae.dom.style.display = '';
39673             ae.dom.style.visibility = 'visible';
39674         }
39675     },
39676
39677     // private
39678     
39679     onHide : function(){
39680         var ae = this.getActionEl();
39681         ae.dom.style.display = 'none';
39682     },
39683
39684     /**
39685      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39686      * by an implementing function.
39687      * @method
39688      * @param {EventObject} e
39689      */
39690     onTriggerClick : Roo.emptyFn
39691 });
39692
39693 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39694 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39695 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39696 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39697     initComponent : function(){
39698         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39699
39700         this.triggerConfig = {
39701             tag:'span', cls:'x-form-twin-triggers', cn:[
39702             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39703             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39704         ]};
39705     },
39706
39707     getTrigger : function(index){
39708         return this.triggers[index];
39709     },
39710
39711     initTrigger : function(){
39712         var ts = this.trigger.select('.x-form-trigger', true);
39713         this.wrap.setStyle('overflow', 'hidden');
39714         var triggerField = this;
39715         ts.each(function(t, all, index){
39716             t.hide = function(){
39717                 var w = triggerField.wrap.getWidth();
39718                 this.dom.style.display = 'none';
39719                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39720             };
39721             t.show = function(){
39722                 var w = triggerField.wrap.getWidth();
39723                 this.dom.style.display = '';
39724                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39725             };
39726             var triggerIndex = 'Trigger'+(index+1);
39727
39728             if(this['hide'+triggerIndex]){
39729                 t.dom.style.display = 'none';
39730             }
39731             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39732             t.addClassOnOver('x-form-trigger-over');
39733             t.addClassOnClick('x-form-trigger-click');
39734         }, this);
39735         this.triggers = ts.elements;
39736     },
39737
39738     onTrigger1Click : Roo.emptyFn,
39739     onTrigger2Click : Roo.emptyFn
39740 });/*
39741  * Based on:
39742  * Ext JS Library 1.1.1
39743  * Copyright(c) 2006-2007, Ext JS, LLC.
39744  *
39745  * Originally Released Under LGPL - original licence link has changed is not relivant.
39746  *
39747  * Fork - LGPL
39748  * <script type="text/javascript">
39749  */
39750  
39751 /**
39752  * @class Roo.form.TextArea
39753  * @extends Roo.form.TextField
39754  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39755  * support for auto-sizing.
39756  * @constructor
39757  * Creates a new TextArea
39758  * @param {Object} config Configuration options
39759  */
39760 Roo.form.TextArea = function(config){
39761     Roo.form.TextArea.superclass.constructor.call(this, config);
39762     // these are provided exchanges for backwards compat
39763     // minHeight/maxHeight were replaced by growMin/growMax to be
39764     // compatible with TextField growing config values
39765     if(this.minHeight !== undefined){
39766         this.growMin = this.minHeight;
39767     }
39768     if(this.maxHeight !== undefined){
39769         this.growMax = this.maxHeight;
39770     }
39771 };
39772
39773 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39774     /**
39775      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39776      */
39777     growMin : 60,
39778     /**
39779      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39780      */
39781     growMax: 1000,
39782     /**
39783      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39784      * in the field (equivalent to setting overflow: hidden, defaults to false)
39785      */
39786     preventScrollbars: false,
39787     /**
39788      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39789      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39790      */
39791
39792     // private
39793     onRender : function(ct, position){
39794         if(!this.el){
39795             this.defaultAutoCreate = {
39796                 tag: "textarea",
39797                 style:"width:300px;height:60px;",
39798                 autocomplete: "new-password"
39799             };
39800         }
39801         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39802         if(this.grow){
39803             this.textSizeEl = Roo.DomHelper.append(document.body, {
39804                 tag: "pre", cls: "x-form-grow-sizer"
39805             });
39806             if(this.preventScrollbars){
39807                 this.el.setStyle("overflow", "hidden");
39808             }
39809             this.el.setHeight(this.growMin);
39810         }
39811     },
39812
39813     onDestroy : function(){
39814         if(this.textSizeEl){
39815             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39816         }
39817         Roo.form.TextArea.superclass.onDestroy.call(this);
39818     },
39819
39820     // private
39821     onKeyUp : function(e){
39822         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39823             this.autoSize();
39824         }
39825     },
39826
39827     /**
39828      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39829      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39830      */
39831     autoSize : function(){
39832         if(!this.grow || !this.textSizeEl){
39833             return;
39834         }
39835         var el = this.el;
39836         var v = el.dom.value;
39837         var ts = this.textSizeEl;
39838
39839         ts.innerHTML = '';
39840         ts.appendChild(document.createTextNode(v));
39841         v = ts.innerHTML;
39842
39843         Roo.fly(ts).setWidth(this.el.getWidth());
39844         if(v.length < 1){
39845             v = "&#160;&#160;";
39846         }else{
39847             if(Roo.isIE){
39848                 v = v.replace(/\n/g, '<p>&#160;</p>');
39849             }
39850             v += "&#160;\n&#160;";
39851         }
39852         ts.innerHTML = v;
39853         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39854         if(h != this.lastHeight){
39855             this.lastHeight = h;
39856             this.el.setHeight(h);
39857             this.fireEvent("autosize", this, h);
39858         }
39859     }
39860 });/*
39861  * Based on:
39862  * Ext JS Library 1.1.1
39863  * Copyright(c) 2006-2007, Ext JS, LLC.
39864  *
39865  * Originally Released Under LGPL - original licence link has changed is not relivant.
39866  *
39867  * Fork - LGPL
39868  * <script type="text/javascript">
39869  */
39870  
39871
39872 /**
39873  * @class Roo.form.NumberField
39874  * @extends Roo.form.TextField
39875  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39876  * @constructor
39877  * Creates a new NumberField
39878  * @param {Object} config Configuration options
39879  */
39880 Roo.form.NumberField = function(config){
39881     Roo.form.NumberField.superclass.constructor.call(this, config);
39882 };
39883
39884 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39885     /**
39886      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39887      */
39888     fieldClass: "x-form-field x-form-num-field",
39889     /**
39890      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39891      */
39892     allowDecimals : true,
39893     /**
39894      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39895      */
39896     decimalSeparator : ".",
39897     /**
39898      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39899      */
39900     decimalPrecision : 2,
39901     /**
39902      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39903      */
39904     allowNegative : true,
39905     /**
39906      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39907      */
39908     minValue : Number.NEGATIVE_INFINITY,
39909     /**
39910      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39911      */
39912     maxValue : Number.MAX_VALUE,
39913     /**
39914      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39915      */
39916     minText : "The minimum value for this field is {0}",
39917     /**
39918      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39919      */
39920     maxText : "The maximum value for this field is {0}",
39921     /**
39922      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39923      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39924      */
39925     nanText : "{0} is not a valid number",
39926
39927     // private
39928     initEvents : function(){
39929         Roo.form.NumberField.superclass.initEvents.call(this);
39930         var allowed = "0123456789";
39931         if(this.allowDecimals){
39932             allowed += this.decimalSeparator;
39933         }
39934         if(this.allowNegative){
39935             allowed += "-";
39936         }
39937         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39938         var keyPress = function(e){
39939             var k = e.getKey();
39940             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39941                 return;
39942             }
39943             var c = e.getCharCode();
39944             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39945                 e.stopEvent();
39946             }
39947         };
39948         this.el.on("keypress", keyPress, this);
39949     },
39950
39951     // private
39952     validateValue : function(value){
39953         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
39954             return false;
39955         }
39956         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39957              return true;
39958         }
39959         var num = this.parseValue(value);
39960         if(isNaN(num)){
39961             this.markInvalid(String.format(this.nanText, value));
39962             return false;
39963         }
39964         if(num < this.minValue){
39965             this.markInvalid(String.format(this.minText, this.minValue));
39966             return false;
39967         }
39968         if(num > this.maxValue){
39969             this.markInvalid(String.format(this.maxText, this.maxValue));
39970             return false;
39971         }
39972         return true;
39973     },
39974
39975     getValue : function(){
39976         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
39977     },
39978
39979     // private
39980     parseValue : function(value){
39981         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39982         return isNaN(value) ? '' : value;
39983     },
39984
39985     // private
39986     fixPrecision : function(value){
39987         var nan = isNaN(value);
39988         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39989             return nan ? '' : value;
39990         }
39991         return parseFloat(value).toFixed(this.decimalPrecision);
39992     },
39993
39994     setValue : function(v){
39995         v = this.fixPrecision(v);
39996         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
39997     },
39998
39999     // private
40000     decimalPrecisionFcn : function(v){
40001         return Math.floor(v);
40002     },
40003
40004     beforeBlur : function(){
40005         var v = this.parseValue(this.getRawValue());
40006         if(v){
40007             this.setValue(v);
40008         }
40009     }
40010 });/*
40011  * Based on:
40012  * Ext JS Library 1.1.1
40013  * Copyright(c) 2006-2007, Ext JS, LLC.
40014  *
40015  * Originally Released Under LGPL - original licence link has changed is not relivant.
40016  *
40017  * Fork - LGPL
40018  * <script type="text/javascript">
40019  */
40020  
40021 /**
40022  * @class Roo.form.DateField
40023  * @extends Roo.form.TriggerField
40024  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40025 * @constructor
40026 * Create a new DateField
40027 * @param {Object} config
40028  */
40029 Roo.form.DateField = function(config){
40030     Roo.form.DateField.superclass.constructor.call(this, config);
40031     
40032       this.addEvents({
40033          
40034         /**
40035          * @event select
40036          * Fires when a date is selected
40037              * @param {Roo.form.DateField} combo This combo box
40038              * @param {Date} date The date selected
40039              */
40040         'select' : true
40041          
40042     });
40043     
40044     
40045     if(typeof this.minValue == "string") {
40046         this.minValue = this.parseDate(this.minValue);
40047     }
40048     if(typeof this.maxValue == "string") {
40049         this.maxValue = this.parseDate(this.maxValue);
40050     }
40051     this.ddMatch = null;
40052     if(this.disabledDates){
40053         var dd = this.disabledDates;
40054         var re = "(?:";
40055         for(var i = 0; i < dd.length; i++){
40056             re += dd[i];
40057             if(i != dd.length-1) {
40058                 re += "|";
40059             }
40060         }
40061         this.ddMatch = new RegExp(re + ")");
40062     }
40063 };
40064
40065 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40066     /**
40067      * @cfg {String} format
40068      * The default date format string which can be overriden for localization support.  The format must be
40069      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40070      */
40071     format : "m/d/y",
40072     /**
40073      * @cfg {String} altFormats
40074      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40075      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40076      */
40077     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40078     /**
40079      * @cfg {Array} disabledDays
40080      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40081      */
40082     disabledDays : null,
40083     /**
40084      * @cfg {String} disabledDaysText
40085      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40086      */
40087     disabledDaysText : "Disabled",
40088     /**
40089      * @cfg {Array} disabledDates
40090      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40091      * expression so they are very powerful. Some examples:
40092      * <ul>
40093      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40094      * <li>["03/08", "09/16"] would disable those days for every year</li>
40095      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40096      * <li>["03/../2006"] would disable every day in March 2006</li>
40097      * <li>["^03"] would disable every day in every March</li>
40098      * </ul>
40099      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40100      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40101      */
40102     disabledDates : null,
40103     /**
40104      * @cfg {String} disabledDatesText
40105      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40106      */
40107     disabledDatesText : "Disabled",
40108     /**
40109      * @cfg {Date/String} minValue
40110      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40111      * valid format (defaults to null).
40112      */
40113     minValue : null,
40114     /**
40115      * @cfg {Date/String} maxValue
40116      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40117      * valid format (defaults to null).
40118      */
40119     maxValue : null,
40120     /**
40121      * @cfg {String} minText
40122      * The error text to display when the date in the cell is before minValue (defaults to
40123      * 'The date in this field must be after {minValue}').
40124      */
40125     minText : "The date in this field must be equal to or after {0}",
40126     /**
40127      * @cfg {String} maxText
40128      * The error text to display when the date in the cell is after maxValue (defaults to
40129      * 'The date in this field must be before {maxValue}').
40130      */
40131     maxText : "The date in this field must be equal to or before {0}",
40132     /**
40133      * @cfg {String} invalidText
40134      * The error text to display when the date in the field is invalid (defaults to
40135      * '{value} is not a valid date - it must be in the format {format}').
40136      */
40137     invalidText : "{0} is not a valid date - it must be in the format {1}",
40138     /**
40139      * @cfg {String} triggerClass
40140      * An additional CSS class used to style the trigger button.  The trigger will always get the
40141      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40142      * which displays a calendar icon).
40143      */
40144     triggerClass : 'x-form-date-trigger',
40145     
40146
40147     /**
40148      * @cfg {Boolean} useIso
40149      * if enabled, then the date field will use a hidden field to store the 
40150      * real value as iso formated date. default (false)
40151      */ 
40152     useIso : false,
40153     /**
40154      * @cfg {String/Object} autoCreate
40155      * A DomHelper element spec, or true for a default element spec (defaults to
40156      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40157      */ 
40158     // private
40159     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40160     
40161     // private
40162     hiddenField: false,
40163     
40164     onRender : function(ct, position)
40165     {
40166         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40167         if (this.useIso) {
40168             //this.el.dom.removeAttribute('name'); 
40169             Roo.log("Changing name?");
40170             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40171             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40172                     'before', true);
40173             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40174             // prevent input submission
40175             this.hiddenName = this.name;
40176         }
40177             
40178             
40179     },
40180     
40181     // private
40182     validateValue : function(value)
40183     {
40184         value = this.formatDate(value);
40185         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40186             Roo.log('super failed');
40187             return false;
40188         }
40189         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40190              return true;
40191         }
40192         var svalue = value;
40193         value = this.parseDate(value);
40194         if(!value){
40195             Roo.log('parse date failed' + svalue);
40196             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40197             return false;
40198         }
40199         var time = value.getTime();
40200         if(this.minValue && time < this.minValue.getTime()){
40201             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40202             return false;
40203         }
40204         if(this.maxValue && time > this.maxValue.getTime()){
40205             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40206             return false;
40207         }
40208         if(this.disabledDays){
40209             var day = value.getDay();
40210             for(var i = 0; i < this.disabledDays.length; i++) {
40211                 if(day === this.disabledDays[i]){
40212                     this.markInvalid(this.disabledDaysText);
40213                     return false;
40214                 }
40215             }
40216         }
40217         var fvalue = this.formatDate(value);
40218         if(this.ddMatch && this.ddMatch.test(fvalue)){
40219             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40220             return false;
40221         }
40222         return true;
40223     },
40224
40225     // private
40226     // Provides logic to override the default TriggerField.validateBlur which just returns true
40227     validateBlur : function(){
40228         return !this.menu || !this.menu.isVisible();
40229     },
40230     
40231     getName: function()
40232     {
40233         // returns hidden if it's set..
40234         if (!this.rendered) {return ''};
40235         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40236         
40237     },
40238
40239     /**
40240      * Returns the current date value of the date field.
40241      * @return {Date} The date value
40242      */
40243     getValue : function(){
40244         
40245         return  this.hiddenField ?
40246                 this.hiddenField.value :
40247                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40248     },
40249
40250     /**
40251      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40252      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40253      * (the default format used is "m/d/y").
40254      * <br />Usage:
40255      * <pre><code>
40256 //All of these calls set the same date value (May 4, 2006)
40257
40258 //Pass a date object:
40259 var dt = new Date('5/4/06');
40260 dateField.setValue(dt);
40261
40262 //Pass a date string (default format):
40263 dateField.setValue('5/4/06');
40264
40265 //Pass a date string (custom format):
40266 dateField.format = 'Y-m-d';
40267 dateField.setValue('2006-5-4');
40268 </code></pre>
40269      * @param {String/Date} date The date or valid date string
40270      */
40271     setValue : function(date){
40272         if (this.hiddenField) {
40273             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40274         }
40275         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40276         // make sure the value field is always stored as a date..
40277         this.value = this.parseDate(date);
40278         
40279         
40280     },
40281
40282     // private
40283     parseDate : function(value){
40284         if(!value || value instanceof Date){
40285             return value;
40286         }
40287         var v = Date.parseDate(value, this.format);
40288          if (!v && this.useIso) {
40289             v = Date.parseDate(value, 'Y-m-d');
40290         }
40291         if(!v && this.altFormats){
40292             if(!this.altFormatsArray){
40293                 this.altFormatsArray = this.altFormats.split("|");
40294             }
40295             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40296                 v = Date.parseDate(value, this.altFormatsArray[i]);
40297             }
40298         }
40299         return v;
40300     },
40301
40302     // private
40303     formatDate : function(date, fmt){
40304         return (!date || !(date instanceof Date)) ?
40305                date : date.dateFormat(fmt || this.format);
40306     },
40307
40308     // private
40309     menuListeners : {
40310         select: function(m, d){
40311             
40312             this.setValue(d);
40313             this.fireEvent('select', this, d);
40314         },
40315         show : function(){ // retain focus styling
40316             this.onFocus();
40317         },
40318         hide : function(){
40319             this.focus.defer(10, this);
40320             var ml = this.menuListeners;
40321             this.menu.un("select", ml.select,  this);
40322             this.menu.un("show", ml.show,  this);
40323             this.menu.un("hide", ml.hide,  this);
40324         }
40325     },
40326
40327     // private
40328     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40329     onTriggerClick : function(){
40330         if(this.disabled){
40331             return;
40332         }
40333         if(this.menu == null){
40334             this.menu = new Roo.menu.DateMenu();
40335         }
40336         Roo.apply(this.menu.picker,  {
40337             showClear: this.allowBlank,
40338             minDate : this.minValue,
40339             maxDate : this.maxValue,
40340             disabledDatesRE : this.ddMatch,
40341             disabledDatesText : this.disabledDatesText,
40342             disabledDays : this.disabledDays,
40343             disabledDaysText : this.disabledDaysText,
40344             format : this.useIso ? 'Y-m-d' : this.format,
40345             minText : String.format(this.minText, this.formatDate(this.minValue)),
40346             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40347         });
40348         this.menu.on(Roo.apply({}, this.menuListeners, {
40349             scope:this
40350         }));
40351         this.menu.picker.setValue(this.getValue() || new Date());
40352         this.menu.show(this.el, "tl-bl?");
40353     },
40354
40355     beforeBlur : function(){
40356         var v = this.parseDate(this.getRawValue());
40357         if(v){
40358             this.setValue(v);
40359         }
40360     },
40361
40362     /*@
40363      * overide
40364      * 
40365      */
40366     isDirty : function() {
40367         if(this.disabled) {
40368             return false;
40369         }
40370         
40371         if(typeof(this.startValue) === 'undefined'){
40372             return false;
40373         }
40374         
40375         return String(this.getValue()) !== String(this.startValue);
40376         
40377     }
40378 });/*
40379  * Based on:
40380  * Ext JS Library 1.1.1
40381  * Copyright(c) 2006-2007, Ext JS, LLC.
40382  *
40383  * Originally Released Under LGPL - original licence link has changed is not relivant.
40384  *
40385  * Fork - LGPL
40386  * <script type="text/javascript">
40387  */
40388  
40389 /**
40390  * @class Roo.form.MonthField
40391  * @extends Roo.form.TriggerField
40392  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40393 * @constructor
40394 * Create a new MonthField
40395 * @param {Object} config
40396  */
40397 Roo.form.MonthField = function(config){
40398     
40399     Roo.form.MonthField.superclass.constructor.call(this, config);
40400     
40401       this.addEvents({
40402          
40403         /**
40404          * @event select
40405          * Fires when a date is selected
40406              * @param {Roo.form.MonthFieeld} combo This combo box
40407              * @param {Date} date The date selected
40408              */
40409         'select' : true
40410          
40411     });
40412     
40413     
40414     if(typeof this.minValue == "string") {
40415         this.minValue = this.parseDate(this.minValue);
40416     }
40417     if(typeof this.maxValue == "string") {
40418         this.maxValue = this.parseDate(this.maxValue);
40419     }
40420     this.ddMatch = null;
40421     if(this.disabledDates){
40422         var dd = this.disabledDates;
40423         var re = "(?:";
40424         for(var i = 0; i < dd.length; i++){
40425             re += dd[i];
40426             if(i != dd.length-1) {
40427                 re += "|";
40428             }
40429         }
40430         this.ddMatch = new RegExp(re + ")");
40431     }
40432 };
40433
40434 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40435     /**
40436      * @cfg {String} format
40437      * The default date format string which can be overriden for localization support.  The format must be
40438      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40439      */
40440     format : "M Y",
40441     /**
40442      * @cfg {String} altFormats
40443      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40444      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40445      */
40446     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40447     /**
40448      * @cfg {Array} disabledDays
40449      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40450      */
40451     disabledDays : [0,1,2,3,4,5,6],
40452     /**
40453      * @cfg {String} disabledDaysText
40454      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40455      */
40456     disabledDaysText : "Disabled",
40457     /**
40458      * @cfg {Array} disabledDates
40459      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40460      * expression so they are very powerful. Some examples:
40461      * <ul>
40462      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40463      * <li>["03/08", "09/16"] would disable those days for every year</li>
40464      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40465      * <li>["03/../2006"] would disable every day in March 2006</li>
40466      * <li>["^03"] would disable every day in every March</li>
40467      * </ul>
40468      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40469      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40470      */
40471     disabledDates : null,
40472     /**
40473      * @cfg {String} disabledDatesText
40474      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40475      */
40476     disabledDatesText : "Disabled",
40477     /**
40478      * @cfg {Date/String} minValue
40479      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40480      * valid format (defaults to null).
40481      */
40482     minValue : null,
40483     /**
40484      * @cfg {Date/String} maxValue
40485      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40486      * valid format (defaults to null).
40487      */
40488     maxValue : null,
40489     /**
40490      * @cfg {String} minText
40491      * The error text to display when the date in the cell is before minValue (defaults to
40492      * 'The date in this field must be after {minValue}').
40493      */
40494     minText : "The date in this field must be equal to or after {0}",
40495     /**
40496      * @cfg {String} maxTextf
40497      * The error text to display when the date in the cell is after maxValue (defaults to
40498      * 'The date in this field must be before {maxValue}').
40499      */
40500     maxText : "The date in this field must be equal to or before {0}",
40501     /**
40502      * @cfg {String} invalidText
40503      * The error text to display when the date in the field is invalid (defaults to
40504      * '{value} is not a valid date - it must be in the format {format}').
40505      */
40506     invalidText : "{0} is not a valid date - it must be in the format {1}",
40507     /**
40508      * @cfg {String} triggerClass
40509      * An additional CSS class used to style the trigger button.  The trigger will always get the
40510      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40511      * which displays a calendar icon).
40512      */
40513     triggerClass : 'x-form-date-trigger',
40514     
40515
40516     /**
40517      * @cfg {Boolean} useIso
40518      * if enabled, then the date field will use a hidden field to store the 
40519      * real value as iso formated date. default (true)
40520      */ 
40521     useIso : true,
40522     /**
40523      * @cfg {String/Object} autoCreate
40524      * A DomHelper element spec, or true for a default element spec (defaults to
40525      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40526      */ 
40527     // private
40528     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40529     
40530     // private
40531     hiddenField: false,
40532     
40533     hideMonthPicker : false,
40534     
40535     onRender : function(ct, position)
40536     {
40537         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40538         if (this.useIso) {
40539             this.el.dom.removeAttribute('name'); 
40540             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40541                     'before', true);
40542             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40543             // prevent input submission
40544             this.hiddenName = this.name;
40545         }
40546             
40547             
40548     },
40549     
40550     // private
40551     validateValue : function(value)
40552     {
40553         value = this.formatDate(value);
40554         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40555             return false;
40556         }
40557         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40558              return true;
40559         }
40560         var svalue = value;
40561         value = this.parseDate(value);
40562         if(!value){
40563             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40564             return false;
40565         }
40566         var time = value.getTime();
40567         if(this.minValue && time < this.minValue.getTime()){
40568             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40569             return false;
40570         }
40571         if(this.maxValue && time > this.maxValue.getTime()){
40572             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40573             return false;
40574         }
40575         /*if(this.disabledDays){
40576             var day = value.getDay();
40577             for(var i = 0; i < this.disabledDays.length; i++) {
40578                 if(day === this.disabledDays[i]){
40579                     this.markInvalid(this.disabledDaysText);
40580                     return false;
40581                 }
40582             }
40583         }
40584         */
40585         var fvalue = this.formatDate(value);
40586         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40587             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40588             return false;
40589         }
40590         */
40591         return true;
40592     },
40593
40594     // private
40595     // Provides logic to override the default TriggerField.validateBlur which just returns true
40596     validateBlur : function(){
40597         return !this.menu || !this.menu.isVisible();
40598     },
40599
40600     /**
40601      * Returns the current date value of the date field.
40602      * @return {Date} The date value
40603      */
40604     getValue : function(){
40605         
40606         
40607         
40608         return  this.hiddenField ?
40609                 this.hiddenField.value :
40610                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40611     },
40612
40613     /**
40614      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40615      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40616      * (the default format used is "m/d/y").
40617      * <br />Usage:
40618      * <pre><code>
40619 //All of these calls set the same date value (May 4, 2006)
40620
40621 //Pass a date object:
40622 var dt = new Date('5/4/06');
40623 monthField.setValue(dt);
40624
40625 //Pass a date string (default format):
40626 monthField.setValue('5/4/06');
40627
40628 //Pass a date string (custom format):
40629 monthField.format = 'Y-m-d';
40630 monthField.setValue('2006-5-4');
40631 </code></pre>
40632      * @param {String/Date} date The date or valid date string
40633      */
40634     setValue : function(date){
40635         Roo.log('month setValue' + date);
40636         // can only be first of month..
40637         
40638         var val = this.parseDate(date);
40639         
40640         if (this.hiddenField) {
40641             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40642         }
40643         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40644         this.value = this.parseDate(date);
40645     },
40646
40647     // private
40648     parseDate : function(value){
40649         if(!value || value instanceof Date){
40650             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40651             return value;
40652         }
40653         var v = Date.parseDate(value, this.format);
40654         if (!v && this.useIso) {
40655             v = Date.parseDate(value, 'Y-m-d');
40656         }
40657         if (v) {
40658             // 
40659             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40660         }
40661         
40662         
40663         if(!v && this.altFormats){
40664             if(!this.altFormatsArray){
40665                 this.altFormatsArray = this.altFormats.split("|");
40666             }
40667             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40668                 v = Date.parseDate(value, this.altFormatsArray[i]);
40669             }
40670         }
40671         return v;
40672     },
40673
40674     // private
40675     formatDate : function(date, fmt){
40676         return (!date || !(date instanceof Date)) ?
40677                date : date.dateFormat(fmt || this.format);
40678     },
40679
40680     // private
40681     menuListeners : {
40682         select: function(m, d){
40683             this.setValue(d);
40684             this.fireEvent('select', this, d);
40685         },
40686         show : function(){ // retain focus styling
40687             this.onFocus();
40688         },
40689         hide : function(){
40690             this.focus.defer(10, this);
40691             var ml = this.menuListeners;
40692             this.menu.un("select", ml.select,  this);
40693             this.menu.un("show", ml.show,  this);
40694             this.menu.un("hide", ml.hide,  this);
40695         }
40696     },
40697     // private
40698     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40699     onTriggerClick : function(){
40700         if(this.disabled){
40701             return;
40702         }
40703         if(this.menu == null){
40704             this.menu = new Roo.menu.DateMenu();
40705            
40706         }
40707         
40708         Roo.apply(this.menu.picker,  {
40709             
40710             showClear: this.allowBlank,
40711             minDate : this.minValue,
40712             maxDate : this.maxValue,
40713             disabledDatesRE : this.ddMatch,
40714             disabledDatesText : this.disabledDatesText,
40715             
40716             format : this.useIso ? 'Y-m-d' : this.format,
40717             minText : String.format(this.minText, this.formatDate(this.minValue)),
40718             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40719             
40720         });
40721          this.menu.on(Roo.apply({}, this.menuListeners, {
40722             scope:this
40723         }));
40724        
40725         
40726         var m = this.menu;
40727         var p = m.picker;
40728         
40729         // hide month picker get's called when we called by 'before hide';
40730         
40731         var ignorehide = true;
40732         p.hideMonthPicker  = function(disableAnim){
40733             if (ignorehide) {
40734                 return;
40735             }
40736              if(this.monthPicker){
40737                 Roo.log("hideMonthPicker called");
40738                 if(disableAnim === true){
40739                     this.monthPicker.hide();
40740                 }else{
40741                     this.monthPicker.slideOut('t', {duration:.2});
40742                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40743                     p.fireEvent("select", this, this.value);
40744                     m.hide();
40745                 }
40746             }
40747         }
40748         
40749         Roo.log('picker set value');
40750         Roo.log(this.getValue());
40751         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40752         m.show(this.el, 'tl-bl?');
40753         ignorehide  = false;
40754         // this will trigger hideMonthPicker..
40755         
40756         
40757         // hidden the day picker
40758         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40759         
40760         
40761         
40762       
40763         
40764         p.showMonthPicker.defer(100, p);
40765     
40766         
40767        
40768     },
40769
40770     beforeBlur : function(){
40771         var v = this.parseDate(this.getRawValue());
40772         if(v){
40773             this.setValue(v);
40774         }
40775     }
40776
40777     /** @cfg {Boolean} grow @hide */
40778     /** @cfg {Number} growMin @hide */
40779     /** @cfg {Number} growMax @hide */
40780     /**
40781      * @hide
40782      * @method autoSize
40783      */
40784 });/*
40785  * Based on:
40786  * Ext JS Library 1.1.1
40787  * Copyright(c) 2006-2007, Ext JS, LLC.
40788  *
40789  * Originally Released Under LGPL - original licence link has changed is not relivant.
40790  *
40791  * Fork - LGPL
40792  * <script type="text/javascript">
40793  */
40794  
40795
40796 /**
40797  * @class Roo.form.ComboBox
40798  * @extends Roo.form.TriggerField
40799  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40800  * @constructor
40801  * Create a new ComboBox.
40802  * @param {Object} config Configuration options
40803  */
40804 Roo.form.ComboBox = function(config){
40805     Roo.form.ComboBox.superclass.constructor.call(this, config);
40806     this.addEvents({
40807         /**
40808          * @event expand
40809          * Fires when the dropdown list is expanded
40810              * @param {Roo.form.ComboBox} combo This combo box
40811              */
40812         'expand' : true,
40813         /**
40814          * @event collapse
40815          * Fires when the dropdown list is collapsed
40816              * @param {Roo.form.ComboBox} combo This combo box
40817              */
40818         'collapse' : true,
40819         /**
40820          * @event beforeselect
40821          * Fires before a list item is selected. Return false to cancel the selection.
40822              * @param {Roo.form.ComboBox} combo This combo box
40823              * @param {Roo.data.Record} record The data record returned from the underlying store
40824              * @param {Number} index The index of the selected item in the dropdown list
40825              */
40826         'beforeselect' : true,
40827         /**
40828          * @event select
40829          * Fires when a list item is selected
40830              * @param {Roo.form.ComboBox} combo This combo box
40831              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40832              * @param {Number} index The index of the selected item in the dropdown list
40833              */
40834         'select' : true,
40835         /**
40836          * @event beforequery
40837          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40838          * The event object passed has these properties:
40839              * @param {Roo.form.ComboBox} combo This combo box
40840              * @param {String} query The query
40841              * @param {Boolean} forceAll true to force "all" query
40842              * @param {Boolean} cancel true to cancel the query
40843              * @param {Object} e The query event object
40844              */
40845         'beforequery': true,
40846          /**
40847          * @event add
40848          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40849              * @param {Roo.form.ComboBox} combo This combo box
40850              */
40851         'add' : true,
40852         /**
40853          * @event edit
40854          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40855              * @param {Roo.form.ComboBox} combo This combo box
40856              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40857              */
40858         'edit' : true
40859         
40860         
40861     });
40862     if(this.transform){
40863         this.allowDomMove = false;
40864         var s = Roo.getDom(this.transform);
40865         if(!this.hiddenName){
40866             this.hiddenName = s.name;
40867         }
40868         if(!this.store){
40869             this.mode = 'local';
40870             var d = [], opts = s.options;
40871             for(var i = 0, len = opts.length;i < len; i++){
40872                 var o = opts[i];
40873                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40874                 if(o.selected) {
40875                     this.value = value;
40876                 }
40877                 d.push([value, o.text]);
40878             }
40879             this.store = new Roo.data.SimpleStore({
40880                 'id': 0,
40881                 fields: ['value', 'text'],
40882                 data : d
40883             });
40884             this.valueField = 'value';
40885             this.displayField = 'text';
40886         }
40887         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40888         if(!this.lazyRender){
40889             this.target = true;
40890             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40891             s.parentNode.removeChild(s); // remove it
40892             this.render(this.el.parentNode);
40893         }else{
40894             s.parentNode.removeChild(s); // remove it
40895         }
40896
40897     }
40898     if (this.store) {
40899         this.store = Roo.factory(this.store, Roo.data);
40900     }
40901     
40902     this.selectedIndex = -1;
40903     if(this.mode == 'local'){
40904         if(config.queryDelay === undefined){
40905             this.queryDelay = 10;
40906         }
40907         if(config.minChars === undefined){
40908             this.minChars = 0;
40909         }
40910     }
40911 };
40912
40913 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40914     /**
40915      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40916      */
40917     /**
40918      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40919      * rendering into an Roo.Editor, defaults to false)
40920      */
40921     /**
40922      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40923      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40924      */
40925     /**
40926      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40927      */
40928     /**
40929      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40930      * the dropdown list (defaults to undefined, with no header element)
40931      */
40932
40933      /**
40934      * @cfg {String/Roo.Template} tpl The template to use to render the output
40935      */
40936      
40937     // private
40938     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40939     /**
40940      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40941      */
40942     listWidth: undefined,
40943     /**
40944      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40945      * mode = 'remote' or 'text' if mode = 'local')
40946      */
40947     displayField: undefined,
40948     /**
40949      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
40950      * mode = 'remote' or 'value' if mode = 'local'). 
40951      * Note: use of a valueField requires the user make a selection
40952      * in order for a value to be mapped.
40953      */
40954     valueField: undefined,
40955     
40956     
40957     /**
40958      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
40959      * field's data value (defaults to the underlying DOM element's name)
40960      */
40961     hiddenName: undefined,
40962     /**
40963      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
40964      */
40965     listClass: '',
40966     /**
40967      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
40968      */
40969     selectedClass: 'x-combo-selected',
40970     /**
40971      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40972      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
40973      * which displays a downward arrow icon).
40974      */
40975     triggerClass : 'x-form-arrow-trigger',
40976     /**
40977      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
40978      */
40979     shadow:'sides',
40980     /**
40981      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
40982      * anchor positions (defaults to 'tl-bl')
40983      */
40984     listAlign: 'tl-bl?',
40985     /**
40986      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
40987      */
40988     maxHeight: 300,
40989     /**
40990      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
40991      * query specified by the allQuery config option (defaults to 'query')
40992      */
40993     triggerAction: 'query',
40994     /**
40995      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
40996      * (defaults to 4, does not apply if editable = false)
40997      */
40998     minChars : 4,
40999     /**
41000      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41001      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41002      */
41003     typeAhead: false,
41004     /**
41005      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41006      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41007      */
41008     queryDelay: 500,
41009     /**
41010      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41011      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41012      */
41013     pageSize: 0,
41014     /**
41015      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41016      * when editable = true (defaults to false)
41017      */
41018     selectOnFocus:false,
41019     /**
41020      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41021      */
41022     queryParam: 'query',
41023     /**
41024      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41025      * when mode = 'remote' (defaults to 'Loading...')
41026      */
41027     loadingText: 'Loading...',
41028     /**
41029      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41030      */
41031     resizable: false,
41032     /**
41033      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41034      */
41035     handleHeight : 8,
41036     /**
41037      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41038      * traditional select (defaults to true)
41039      */
41040     editable: true,
41041     /**
41042      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41043      */
41044     allQuery: '',
41045     /**
41046      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41047      */
41048     mode: 'remote',
41049     /**
41050      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41051      * listWidth has a higher value)
41052      */
41053     minListWidth : 70,
41054     /**
41055      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41056      * allow the user to set arbitrary text into the field (defaults to false)
41057      */
41058     forceSelection:false,
41059     /**
41060      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41061      * if typeAhead = true (defaults to 250)
41062      */
41063     typeAheadDelay : 250,
41064     /**
41065      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41066      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41067      */
41068     valueNotFoundText : undefined,
41069     /**
41070      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41071      */
41072     blockFocus : false,
41073     
41074     /**
41075      * @cfg {Boolean} disableClear Disable showing of clear button.
41076      */
41077     disableClear : false,
41078     /**
41079      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41080      */
41081     alwaysQuery : false,
41082     
41083     //private
41084     addicon : false,
41085     editicon: false,
41086     
41087     // element that contains real text value.. (when hidden is used..)
41088      
41089     // private
41090     onRender : function(ct, position){
41091         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41092         if(this.hiddenName){
41093             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41094                     'before', true);
41095             this.hiddenField.value =
41096                 this.hiddenValue !== undefined ? this.hiddenValue :
41097                 this.value !== undefined ? this.value : '';
41098
41099             // prevent input submission
41100             this.el.dom.removeAttribute('name');
41101              
41102              
41103         }
41104         if(Roo.isGecko){
41105             this.el.dom.setAttribute('autocomplete', 'off');
41106         }
41107
41108         var cls = 'x-combo-list';
41109
41110         this.list = new Roo.Layer({
41111             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41112         });
41113
41114         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41115         this.list.setWidth(lw);
41116         this.list.swallowEvent('mousewheel');
41117         this.assetHeight = 0;
41118
41119         if(this.title){
41120             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41121             this.assetHeight += this.header.getHeight();
41122         }
41123
41124         this.innerList = this.list.createChild({cls:cls+'-inner'});
41125         this.innerList.on('mouseover', this.onViewOver, this);
41126         this.innerList.on('mousemove', this.onViewMove, this);
41127         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41128         
41129         if(this.allowBlank && !this.pageSize && !this.disableClear){
41130             this.footer = this.list.createChild({cls:cls+'-ft'});
41131             this.pageTb = new Roo.Toolbar(this.footer);
41132            
41133         }
41134         if(this.pageSize){
41135             this.footer = this.list.createChild({cls:cls+'-ft'});
41136             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41137                     {pageSize: this.pageSize});
41138             
41139         }
41140         
41141         if (this.pageTb && this.allowBlank && !this.disableClear) {
41142             var _this = this;
41143             this.pageTb.add(new Roo.Toolbar.Fill(), {
41144                 cls: 'x-btn-icon x-btn-clear',
41145                 text: '&#160;',
41146                 handler: function()
41147                 {
41148                     _this.collapse();
41149                     _this.clearValue();
41150                     _this.onSelect(false, -1);
41151                 }
41152             });
41153         }
41154         if (this.footer) {
41155             this.assetHeight += this.footer.getHeight();
41156         }
41157         
41158
41159         if(!this.tpl){
41160             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41161         }
41162
41163         this.view = new Roo.View(this.innerList, this.tpl, {
41164             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41165         });
41166
41167         this.view.on('click', this.onViewClick, this);
41168
41169         this.store.on('beforeload', this.onBeforeLoad, this);
41170         this.store.on('load', this.onLoad, this);
41171         this.store.on('loadexception', this.onLoadException, this);
41172
41173         if(this.resizable){
41174             this.resizer = new Roo.Resizable(this.list,  {
41175                pinned:true, handles:'se'
41176             });
41177             this.resizer.on('resize', function(r, w, h){
41178                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41179                 this.listWidth = w;
41180                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41181                 this.restrictHeight();
41182             }, this);
41183             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41184         }
41185         if(!this.editable){
41186             this.editable = true;
41187             this.setEditable(false);
41188         }  
41189         
41190         
41191         if (typeof(this.events.add.listeners) != 'undefined') {
41192             
41193             this.addicon = this.wrap.createChild(
41194                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41195        
41196             this.addicon.on('click', function(e) {
41197                 this.fireEvent('add', this);
41198             }, this);
41199         }
41200         if (typeof(this.events.edit.listeners) != 'undefined') {
41201             
41202             this.editicon = this.wrap.createChild(
41203                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41204             if (this.addicon) {
41205                 this.editicon.setStyle('margin-left', '40px');
41206             }
41207             this.editicon.on('click', function(e) {
41208                 
41209                 // we fire even  if inothing is selected..
41210                 this.fireEvent('edit', this, this.lastData );
41211                 
41212             }, this);
41213         }
41214         
41215         
41216         
41217     },
41218
41219     // private
41220     initEvents : function(){
41221         Roo.form.ComboBox.superclass.initEvents.call(this);
41222
41223         this.keyNav = new Roo.KeyNav(this.el, {
41224             "up" : function(e){
41225                 this.inKeyMode = true;
41226                 this.selectPrev();
41227             },
41228
41229             "down" : function(e){
41230                 if(!this.isExpanded()){
41231                     this.onTriggerClick();
41232                 }else{
41233                     this.inKeyMode = true;
41234                     this.selectNext();
41235                 }
41236             },
41237
41238             "enter" : function(e){
41239                 this.onViewClick();
41240                 //return true;
41241             },
41242
41243             "esc" : function(e){
41244                 this.collapse();
41245             },
41246
41247             "tab" : function(e){
41248                 this.onViewClick(false);
41249                 this.fireEvent("specialkey", this, e);
41250                 return true;
41251             },
41252
41253             scope : this,
41254
41255             doRelay : function(foo, bar, hname){
41256                 if(hname == 'down' || this.scope.isExpanded()){
41257                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41258                 }
41259                 return true;
41260             },
41261
41262             forceKeyDown: true
41263         });
41264         this.queryDelay = Math.max(this.queryDelay || 10,
41265                 this.mode == 'local' ? 10 : 250);
41266         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41267         if(this.typeAhead){
41268             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41269         }
41270         if(this.editable !== false){
41271             this.el.on("keyup", this.onKeyUp, this);
41272         }
41273         if(this.forceSelection){
41274             this.on('blur', this.doForce, this);
41275         }
41276     },
41277
41278     onDestroy : function(){
41279         if(this.view){
41280             this.view.setStore(null);
41281             this.view.el.removeAllListeners();
41282             this.view.el.remove();
41283             this.view.purgeListeners();
41284         }
41285         if(this.list){
41286             this.list.destroy();
41287         }
41288         if(this.store){
41289             this.store.un('beforeload', this.onBeforeLoad, this);
41290             this.store.un('load', this.onLoad, this);
41291             this.store.un('loadexception', this.onLoadException, this);
41292         }
41293         Roo.form.ComboBox.superclass.onDestroy.call(this);
41294     },
41295
41296     // private
41297     fireKey : function(e){
41298         if(e.isNavKeyPress() && !this.list.isVisible()){
41299             this.fireEvent("specialkey", this, e);
41300         }
41301     },
41302
41303     // private
41304     onResize: function(w, h){
41305         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41306         
41307         if(typeof w != 'number'){
41308             // we do not handle it!?!?
41309             return;
41310         }
41311         var tw = this.trigger.getWidth();
41312         tw += this.addicon ? this.addicon.getWidth() : 0;
41313         tw += this.editicon ? this.editicon.getWidth() : 0;
41314         var x = w - tw;
41315         this.el.setWidth( this.adjustWidth('input', x));
41316             
41317         this.trigger.setStyle('left', x+'px');
41318         
41319         if(this.list && this.listWidth === undefined){
41320             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41321             this.list.setWidth(lw);
41322             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41323         }
41324         
41325     
41326         
41327     },
41328
41329     /**
41330      * Allow or prevent the user from directly editing the field text.  If false is passed,
41331      * the user will only be able to select from the items defined in the dropdown list.  This method
41332      * is the runtime equivalent of setting the 'editable' config option at config time.
41333      * @param {Boolean} value True to allow the user to directly edit the field text
41334      */
41335     setEditable : function(value){
41336         if(value == this.editable){
41337             return;
41338         }
41339         this.editable = value;
41340         if(!value){
41341             this.el.dom.setAttribute('readOnly', true);
41342             this.el.on('mousedown', this.onTriggerClick,  this);
41343             this.el.addClass('x-combo-noedit');
41344         }else{
41345             this.el.dom.setAttribute('readOnly', false);
41346             this.el.un('mousedown', this.onTriggerClick,  this);
41347             this.el.removeClass('x-combo-noedit');
41348         }
41349     },
41350
41351     // private
41352     onBeforeLoad : function(){
41353         if(!this.hasFocus){
41354             return;
41355         }
41356         this.innerList.update(this.loadingText ?
41357                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41358         this.restrictHeight();
41359         this.selectedIndex = -1;
41360     },
41361
41362     // private
41363     onLoad : function(){
41364         if(!this.hasFocus){
41365             return;
41366         }
41367         if(this.store.getCount() > 0){
41368             this.expand();
41369             this.restrictHeight();
41370             if(this.lastQuery == this.allQuery){
41371                 if(this.editable){
41372                     this.el.dom.select();
41373                 }
41374                 if(!this.selectByValue(this.value, true)){
41375                     this.select(0, true);
41376                 }
41377             }else{
41378                 this.selectNext();
41379                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41380                     this.taTask.delay(this.typeAheadDelay);
41381                 }
41382             }
41383         }else{
41384             this.onEmptyResults();
41385         }
41386         //this.el.focus();
41387     },
41388     // private
41389     onLoadException : function()
41390     {
41391         this.collapse();
41392         Roo.log(this.store.reader.jsonData);
41393         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41394             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41395         }
41396         
41397         
41398     },
41399     // private
41400     onTypeAhead : function(){
41401         if(this.store.getCount() > 0){
41402             var r = this.store.getAt(0);
41403             var newValue = r.data[this.displayField];
41404             var len = newValue.length;
41405             var selStart = this.getRawValue().length;
41406             if(selStart != len){
41407                 this.setRawValue(newValue);
41408                 this.selectText(selStart, newValue.length);
41409             }
41410         }
41411     },
41412
41413     // private
41414     onSelect : function(record, index){
41415         if(this.fireEvent('beforeselect', this, record, index) !== false){
41416             this.setFromData(index > -1 ? record.data : false);
41417             this.collapse();
41418             this.fireEvent('select', this, record, index);
41419         }
41420     },
41421
41422     /**
41423      * Returns the currently selected field value or empty string if no value is set.
41424      * @return {String} value The selected value
41425      */
41426     getValue : function(){
41427         if(this.valueField){
41428             return typeof this.value != 'undefined' ? this.value : '';
41429         }
41430         return Roo.form.ComboBox.superclass.getValue.call(this);
41431     },
41432
41433     /**
41434      * Clears any text/value currently set in the field
41435      */
41436     clearValue : function(){
41437         if(this.hiddenField){
41438             this.hiddenField.value = '';
41439         }
41440         this.value = '';
41441         this.setRawValue('');
41442         this.lastSelectionText = '';
41443         
41444     },
41445
41446     /**
41447      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41448      * will be displayed in the field.  If the value does not match the data value of an existing item,
41449      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41450      * Otherwise the field will be blank (although the value will still be set).
41451      * @param {String} value The value to match
41452      */
41453     setValue : function(v){
41454         var text = v;
41455         if(this.valueField){
41456             var r = this.findRecord(this.valueField, v);
41457             if(r){
41458                 text = r.data[this.displayField];
41459             }else if(this.valueNotFoundText !== undefined){
41460                 text = this.valueNotFoundText;
41461             }
41462         }
41463         this.lastSelectionText = text;
41464         if(this.hiddenField){
41465             this.hiddenField.value = v;
41466         }
41467         Roo.form.ComboBox.superclass.setValue.call(this, text);
41468         this.value = v;
41469     },
41470     /**
41471      * @property {Object} the last set data for the element
41472      */
41473     
41474     lastData : false,
41475     /**
41476      * Sets the value of the field based on a object which is related to the record format for the store.
41477      * @param {Object} value the value to set as. or false on reset?
41478      */
41479     setFromData : function(o){
41480         var dv = ''; // display value
41481         var vv = ''; // value value..
41482         this.lastData = o;
41483         if (this.displayField) {
41484             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41485         } else {
41486             // this is an error condition!!!
41487             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41488         }
41489         
41490         if(this.valueField){
41491             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41492         }
41493         if(this.hiddenField){
41494             this.hiddenField.value = vv;
41495             
41496             this.lastSelectionText = dv;
41497             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41498             this.value = vv;
41499             return;
41500         }
41501         // no hidden field.. - we store the value in 'value', but still display
41502         // display field!!!!
41503         this.lastSelectionText = dv;
41504         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41505         this.value = vv;
41506         
41507         
41508     },
41509     // private
41510     reset : function(){
41511         // overridden so that last data is reset..
41512         this.setValue(this.resetValue);
41513         this.clearInvalid();
41514         this.lastData = false;
41515         if (this.view) {
41516             this.view.clearSelections();
41517         }
41518     },
41519     // private
41520     findRecord : function(prop, value){
41521         var record;
41522         if(this.store.getCount() > 0){
41523             this.store.each(function(r){
41524                 if(r.data[prop] == value){
41525                     record = r;
41526                     return false;
41527                 }
41528                 return true;
41529             });
41530         }
41531         return record;
41532     },
41533     
41534     getName: function()
41535     {
41536         // returns hidden if it's set..
41537         if (!this.rendered) {return ''};
41538         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41539         
41540     },
41541     // private
41542     onViewMove : function(e, t){
41543         this.inKeyMode = false;
41544     },
41545
41546     // private
41547     onViewOver : function(e, t){
41548         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41549             return;
41550         }
41551         var item = this.view.findItemFromChild(t);
41552         if(item){
41553             var index = this.view.indexOf(item);
41554             this.select(index, false);
41555         }
41556     },
41557
41558     // private
41559     onViewClick : function(doFocus)
41560     {
41561         var index = this.view.getSelectedIndexes()[0];
41562         var r = this.store.getAt(index);
41563         if(r){
41564             this.onSelect(r, index);
41565         }
41566         if(doFocus !== false && !this.blockFocus){
41567             this.el.focus();
41568         }
41569     },
41570
41571     // private
41572     restrictHeight : function(){
41573         this.innerList.dom.style.height = '';
41574         var inner = this.innerList.dom;
41575         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41576         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41577         this.list.beginUpdate();
41578         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41579         this.list.alignTo(this.el, this.listAlign);
41580         this.list.endUpdate();
41581     },
41582
41583     // private
41584     onEmptyResults : function(){
41585         this.collapse();
41586     },
41587
41588     /**
41589      * Returns true if the dropdown list is expanded, else false.
41590      */
41591     isExpanded : function(){
41592         return this.list.isVisible();
41593     },
41594
41595     /**
41596      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41597      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41598      * @param {String} value The data value of the item to select
41599      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41600      * selected item if it is not currently in view (defaults to true)
41601      * @return {Boolean} True if the value matched an item in the list, else false
41602      */
41603     selectByValue : function(v, scrollIntoView){
41604         if(v !== undefined && v !== null){
41605             var r = this.findRecord(this.valueField || this.displayField, v);
41606             if(r){
41607                 this.select(this.store.indexOf(r), scrollIntoView);
41608                 return true;
41609             }
41610         }
41611         return false;
41612     },
41613
41614     /**
41615      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41616      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41617      * @param {Number} index The zero-based index of the list item to select
41618      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41619      * selected item if it is not currently in view (defaults to true)
41620      */
41621     select : function(index, scrollIntoView){
41622         this.selectedIndex = index;
41623         this.view.select(index);
41624         if(scrollIntoView !== false){
41625             var el = this.view.getNode(index);
41626             if(el){
41627                 this.innerList.scrollChildIntoView(el, false);
41628             }
41629         }
41630     },
41631
41632     // private
41633     selectNext : function(){
41634         var ct = this.store.getCount();
41635         if(ct > 0){
41636             if(this.selectedIndex == -1){
41637                 this.select(0);
41638             }else if(this.selectedIndex < ct-1){
41639                 this.select(this.selectedIndex+1);
41640             }
41641         }
41642     },
41643
41644     // private
41645     selectPrev : function(){
41646         var ct = this.store.getCount();
41647         if(ct > 0){
41648             if(this.selectedIndex == -1){
41649                 this.select(0);
41650             }else if(this.selectedIndex != 0){
41651                 this.select(this.selectedIndex-1);
41652             }
41653         }
41654     },
41655
41656     // private
41657     onKeyUp : function(e){
41658         if(this.editable !== false && !e.isSpecialKey()){
41659             this.lastKey = e.getKey();
41660             this.dqTask.delay(this.queryDelay);
41661         }
41662     },
41663
41664     // private
41665     validateBlur : function(){
41666         return !this.list || !this.list.isVisible();   
41667     },
41668
41669     // private
41670     initQuery : function(){
41671         this.doQuery(this.getRawValue());
41672     },
41673
41674     // private
41675     doForce : function(){
41676         if(this.el.dom.value.length > 0){
41677             this.el.dom.value =
41678                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41679              
41680         }
41681     },
41682
41683     /**
41684      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41685      * query allowing the query action to be canceled if needed.
41686      * @param {String} query The SQL query to execute
41687      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41688      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41689      * saved in the current store (defaults to false)
41690      */
41691     doQuery : function(q, forceAll){
41692         if(q === undefined || q === null){
41693             q = '';
41694         }
41695         var qe = {
41696             query: q,
41697             forceAll: forceAll,
41698             combo: this,
41699             cancel:false
41700         };
41701         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41702             return false;
41703         }
41704         q = qe.query;
41705         forceAll = qe.forceAll;
41706         if(forceAll === true || (q.length >= this.minChars)){
41707             if(this.lastQuery != q || this.alwaysQuery){
41708                 this.lastQuery = q;
41709                 if(this.mode == 'local'){
41710                     this.selectedIndex = -1;
41711                     if(forceAll){
41712                         this.store.clearFilter();
41713                     }else{
41714                         this.store.filter(this.displayField, q);
41715                     }
41716                     this.onLoad();
41717                 }else{
41718                     this.store.baseParams[this.queryParam] = q;
41719                     this.store.load({
41720                         params: this.getParams(q)
41721                     });
41722                     this.expand();
41723                 }
41724             }else{
41725                 this.selectedIndex = -1;
41726                 this.onLoad();   
41727             }
41728         }
41729     },
41730
41731     // private
41732     getParams : function(q){
41733         var p = {};
41734         //p[this.queryParam] = q;
41735         if(this.pageSize){
41736             p.start = 0;
41737             p.limit = this.pageSize;
41738         }
41739         return p;
41740     },
41741
41742     /**
41743      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41744      */
41745     collapse : function(){
41746         if(!this.isExpanded()){
41747             return;
41748         }
41749         this.list.hide();
41750         Roo.get(document).un('mousedown', this.collapseIf, this);
41751         Roo.get(document).un('mousewheel', this.collapseIf, this);
41752         if (!this.editable) {
41753             Roo.get(document).un('keydown', this.listKeyPress, this);
41754         }
41755         this.fireEvent('collapse', this);
41756     },
41757
41758     // private
41759     collapseIf : function(e){
41760         if(!e.within(this.wrap) && !e.within(this.list)){
41761             this.collapse();
41762         }
41763     },
41764
41765     /**
41766      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41767      */
41768     expand : function(){
41769         if(this.isExpanded() || !this.hasFocus){
41770             return;
41771         }
41772         this.list.alignTo(this.el, this.listAlign);
41773         this.list.show();
41774         Roo.get(document).on('mousedown', this.collapseIf, this);
41775         Roo.get(document).on('mousewheel', this.collapseIf, this);
41776         if (!this.editable) {
41777             Roo.get(document).on('keydown', this.listKeyPress, this);
41778         }
41779         
41780         this.fireEvent('expand', this);
41781     },
41782
41783     // private
41784     // Implements the default empty TriggerField.onTriggerClick function
41785     onTriggerClick : function(){
41786         if(this.disabled){
41787             return;
41788         }
41789         if(this.isExpanded()){
41790             this.collapse();
41791             if (!this.blockFocus) {
41792                 this.el.focus();
41793             }
41794             
41795         }else {
41796             this.hasFocus = true;
41797             if(this.triggerAction == 'all') {
41798                 this.doQuery(this.allQuery, true);
41799             } else {
41800                 this.doQuery(this.getRawValue());
41801             }
41802             if (!this.blockFocus) {
41803                 this.el.focus();
41804             }
41805         }
41806     },
41807     listKeyPress : function(e)
41808     {
41809         //Roo.log('listkeypress');
41810         // scroll to first matching element based on key pres..
41811         if (e.isSpecialKey()) {
41812             return false;
41813         }
41814         var k = String.fromCharCode(e.getKey()).toUpperCase();
41815         //Roo.log(k);
41816         var match  = false;
41817         var csel = this.view.getSelectedNodes();
41818         var cselitem = false;
41819         if (csel.length) {
41820             var ix = this.view.indexOf(csel[0]);
41821             cselitem  = this.store.getAt(ix);
41822             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41823                 cselitem = false;
41824             }
41825             
41826         }
41827         
41828         this.store.each(function(v) { 
41829             if (cselitem) {
41830                 // start at existing selection.
41831                 if (cselitem.id == v.id) {
41832                     cselitem = false;
41833                 }
41834                 return;
41835             }
41836                 
41837             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41838                 match = this.store.indexOf(v);
41839                 return false;
41840             }
41841         }, this);
41842         
41843         if (match === false) {
41844             return true; // no more action?
41845         }
41846         // scroll to?
41847         this.view.select(match);
41848         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41849         sn.scrollIntoView(sn.dom.parentNode, false);
41850     }
41851
41852     /** 
41853     * @cfg {Boolean} grow 
41854     * @hide 
41855     */
41856     /** 
41857     * @cfg {Number} growMin 
41858     * @hide 
41859     */
41860     /** 
41861     * @cfg {Number} growMax 
41862     * @hide 
41863     */
41864     /**
41865      * @hide
41866      * @method autoSize
41867      */
41868 });/*
41869  * Copyright(c) 2010-2012, Roo J Solutions Limited
41870  *
41871  * Licence LGPL
41872  *
41873  */
41874
41875 /**
41876  * @class Roo.form.ComboBoxArray
41877  * @extends Roo.form.TextField
41878  * A facebook style adder... for lists of email / people / countries  etc...
41879  * pick multiple items from a combo box, and shows each one.
41880  *
41881  *  Fred [x]  Brian [x]  [Pick another |v]
41882  *
41883  *
41884  *  For this to work: it needs various extra information
41885  *    - normal combo problay has
41886  *      name, hiddenName
41887  *    + displayField, valueField
41888  *
41889  *    For our purpose...
41890  *
41891  *
41892  *   If we change from 'extends' to wrapping...
41893  *   
41894  *  
41895  *
41896  
41897  
41898  * @constructor
41899  * Create a new ComboBoxArray.
41900  * @param {Object} config Configuration options
41901  */
41902  
41903
41904 Roo.form.ComboBoxArray = function(config)
41905 {
41906     this.addEvents({
41907         /**
41908          * @event beforeremove
41909          * Fires before remove the value from the list
41910              * @param {Roo.form.ComboBoxArray} _self This combo box array
41911              * @param {Roo.form.ComboBoxArray.Item} item removed item
41912              */
41913         'beforeremove' : true,
41914         /**
41915          * @event remove
41916          * Fires when remove the value from the list
41917              * @param {Roo.form.ComboBoxArray} _self This combo box array
41918              * @param {Roo.form.ComboBoxArray.Item} item removed item
41919              */
41920         'remove' : true
41921         
41922         
41923     });
41924     
41925     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41926     
41927     this.items = new Roo.util.MixedCollection(false);
41928     
41929     // construct the child combo...
41930     
41931     
41932     
41933     
41934    
41935     
41936 }
41937
41938  
41939 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41940
41941     /**
41942      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41943      */
41944     
41945     lastData : false,
41946     
41947     // behavies liek a hiddne field
41948     inputType:      'hidden',
41949     /**
41950      * @cfg {Number} width The width of the box that displays the selected element
41951      */ 
41952     width:          300,
41953
41954     
41955     
41956     /**
41957      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
41958      */
41959     name : false,
41960     /**
41961      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
41962      */
41963     hiddenName : false,
41964     
41965     
41966     // private the array of items that are displayed..
41967     items  : false,
41968     // private - the hidden field el.
41969     hiddenEl : false,
41970     // private - the filed el..
41971     el : false,
41972     
41973     //validateValue : function() { return true; }, // all values are ok!
41974     //onAddClick: function() { },
41975     
41976     onRender : function(ct, position) 
41977     {
41978         
41979         // create the standard hidden element
41980         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
41981         
41982         
41983         // give fake names to child combo;
41984         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
41985         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
41986         
41987         this.combo = Roo.factory(this.combo, Roo.form);
41988         this.combo.onRender(ct, position);
41989         if (typeof(this.combo.width) != 'undefined') {
41990             this.combo.onResize(this.combo.width,0);
41991         }
41992         
41993         this.combo.initEvents();
41994         
41995         // assigned so form know we need to do this..
41996         this.store          = this.combo.store;
41997         this.valueField     = this.combo.valueField;
41998         this.displayField   = this.combo.displayField ;
41999         
42000         
42001         this.combo.wrap.addClass('x-cbarray-grp');
42002         
42003         var cbwrap = this.combo.wrap.createChild(
42004             {tag: 'div', cls: 'x-cbarray-cb'},
42005             this.combo.el.dom
42006         );
42007         
42008              
42009         this.hiddenEl = this.combo.wrap.createChild({
42010             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42011         });
42012         this.el = this.combo.wrap.createChild({
42013             tag: 'input',  type:'hidden' , name: this.name, value : ''
42014         });
42015          //   this.el.dom.removeAttribute("name");
42016         
42017         
42018         this.outerWrap = this.combo.wrap;
42019         this.wrap = cbwrap;
42020         
42021         this.outerWrap.setWidth(this.width);
42022         this.outerWrap.dom.removeChild(this.el.dom);
42023         
42024         this.wrap.dom.appendChild(this.el.dom);
42025         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42026         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42027         
42028         this.combo.trigger.setStyle('position','relative');
42029         this.combo.trigger.setStyle('left', '0px');
42030         this.combo.trigger.setStyle('top', '2px');
42031         
42032         this.combo.el.setStyle('vertical-align', 'text-bottom');
42033         
42034         //this.trigger.setStyle('vertical-align', 'top');
42035         
42036         // this should use the code from combo really... on('add' ....)
42037         if (this.adder) {
42038             
42039         
42040             this.adder = this.outerWrap.createChild(
42041                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42042             var _t = this;
42043             this.adder.on('click', function(e) {
42044                 _t.fireEvent('adderclick', this, e);
42045             }, _t);
42046         }
42047         //var _t = this;
42048         //this.adder.on('click', this.onAddClick, _t);
42049         
42050         
42051         this.combo.on('select', function(cb, rec, ix) {
42052             this.addItem(rec.data);
42053             
42054             cb.setValue('');
42055             cb.el.dom.value = '';
42056             //cb.lastData = rec.data;
42057             // add to list
42058             
42059         }, this);
42060         
42061         
42062     },
42063     
42064     
42065     getName: function()
42066     {
42067         // returns hidden if it's set..
42068         if (!this.rendered) {return ''};
42069         return  this.hiddenName ? this.hiddenName : this.name;
42070         
42071     },
42072     
42073     
42074     onResize: function(w, h){
42075         
42076         return;
42077         // not sure if this is needed..
42078         //this.combo.onResize(w,h);
42079         
42080         if(typeof w != 'number'){
42081             // we do not handle it!?!?
42082             return;
42083         }
42084         var tw = this.combo.trigger.getWidth();
42085         tw += this.addicon ? this.addicon.getWidth() : 0;
42086         tw += this.editicon ? this.editicon.getWidth() : 0;
42087         var x = w - tw;
42088         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42089             
42090         this.combo.trigger.setStyle('left', '0px');
42091         
42092         if(this.list && this.listWidth === undefined){
42093             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42094             this.list.setWidth(lw);
42095             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42096         }
42097         
42098     
42099         
42100     },
42101     
42102     addItem: function(rec)
42103     {
42104         var valueField = this.combo.valueField;
42105         var displayField = this.combo.displayField;
42106         if (this.items.indexOfKey(rec[valueField]) > -1) {
42107             //console.log("GOT " + rec.data.id);
42108             return;
42109         }
42110         
42111         var x = new Roo.form.ComboBoxArray.Item({
42112             //id : rec[this.idField],
42113             data : rec,
42114             displayField : displayField ,
42115             tipField : displayField ,
42116             cb : this
42117         });
42118         // use the 
42119         this.items.add(rec[valueField],x);
42120         // add it before the element..
42121         this.updateHiddenEl();
42122         x.render(this.outerWrap, this.wrap.dom);
42123         // add the image handler..
42124     },
42125     
42126     updateHiddenEl : function()
42127     {
42128         this.validate();
42129         if (!this.hiddenEl) {
42130             return;
42131         }
42132         var ar = [];
42133         var idField = this.combo.valueField;
42134         
42135         this.items.each(function(f) {
42136             ar.push(f.data[idField]);
42137            
42138         });
42139         this.hiddenEl.dom.value = ar.join(',');
42140         this.validate();
42141     },
42142     
42143     reset : function()
42144     {
42145         this.items.clear();
42146         
42147         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42148            el.remove();
42149         });
42150         
42151         this.el.dom.value = '';
42152         if (this.hiddenEl) {
42153             this.hiddenEl.dom.value = '';
42154         }
42155         
42156     },
42157     getValue: function()
42158     {
42159         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42160     },
42161     setValue: function(v) // not a valid action - must use addItems..
42162     {
42163          
42164         this.reset();
42165         
42166         
42167         
42168         if (this.store.isLocal && (typeof(v) == 'string')) {
42169             // then we can use the store to find the values..
42170             // comma seperated at present.. this needs to allow JSON based encoding..
42171             this.hiddenEl.value  = v;
42172             var v_ar = [];
42173             Roo.each(v.split(','), function(k) {
42174                 Roo.log("CHECK " + this.valueField + ',' + k);
42175                 var li = this.store.query(this.valueField, k);
42176                 if (!li.length) {
42177                     return;
42178                 }
42179                 var add = {};
42180                 add[this.valueField] = k;
42181                 add[this.displayField] = li.item(0).data[this.displayField];
42182                 
42183                 this.addItem(add);
42184             }, this) 
42185              
42186         }
42187         if (typeof(v) == 'object' ) {
42188             // then let's assume it's an array of objects..
42189             Roo.each(v, function(l) {
42190                 this.addItem(l);
42191             }, this);
42192              
42193         }
42194         
42195         
42196     },
42197     setFromData: function(v)
42198     {
42199         // this recieves an object, if setValues is called.
42200         this.reset();
42201         this.el.dom.value = v[this.displayField];
42202         this.hiddenEl.dom.value = v[this.valueField];
42203         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42204             return;
42205         }
42206         var kv = v[this.valueField];
42207         var dv = v[this.displayField];
42208         kv = typeof(kv) != 'string' ? '' : kv;
42209         dv = typeof(dv) != 'string' ? '' : dv;
42210         
42211         
42212         var keys = kv.split(',');
42213         var display = dv.split(',');
42214         for (var i = 0 ; i < keys.length; i++) {
42215             
42216             add = {};
42217             add[this.valueField] = keys[i];
42218             add[this.displayField] = display[i];
42219             this.addItem(add);
42220         }
42221       
42222         
42223     },
42224     
42225     /**
42226      * Validates the combox array value
42227      * @return {Boolean} True if the value is valid, else false
42228      */
42229     validate : function(){
42230         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42231             this.clearInvalid();
42232             return true;
42233         }
42234         return false;
42235     },
42236     
42237     validateValue : function(value){
42238         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42239         
42240     },
42241     
42242     /*@
42243      * overide
42244      * 
42245      */
42246     isDirty : function() {
42247         if(this.disabled) {
42248             return false;
42249         }
42250         
42251         try {
42252             var d = Roo.decode(String(this.originalValue));
42253         } catch (e) {
42254             return String(this.getValue()) !== String(this.originalValue);
42255         }
42256         
42257         var originalValue = [];
42258         
42259         for (var i = 0; i < d.length; i++){
42260             originalValue.push(d[i][this.valueField]);
42261         }
42262         
42263         return String(this.getValue()) !== String(originalValue.join(','));
42264         
42265     }
42266     
42267 });
42268
42269
42270
42271 /**
42272  * @class Roo.form.ComboBoxArray.Item
42273  * @extends Roo.BoxComponent
42274  * A selected item in the list
42275  *  Fred [x]  Brian [x]  [Pick another |v]
42276  * 
42277  * @constructor
42278  * Create a new item.
42279  * @param {Object} config Configuration options
42280  */
42281  
42282 Roo.form.ComboBoxArray.Item = function(config) {
42283     config.id = Roo.id();
42284     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42285 }
42286
42287 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42288     data : {},
42289     cb: false,
42290     displayField : false,
42291     tipField : false,
42292     
42293     
42294     defaultAutoCreate : {
42295         tag: 'div',
42296         cls: 'x-cbarray-item',
42297         cn : [ 
42298             { tag: 'div' },
42299             {
42300                 tag: 'img',
42301                 width:16,
42302                 height : 16,
42303                 src : Roo.BLANK_IMAGE_URL ,
42304                 align: 'center'
42305             }
42306         ]
42307         
42308     },
42309     
42310  
42311     onRender : function(ct, position)
42312     {
42313         Roo.form.Field.superclass.onRender.call(this, ct, position);
42314         
42315         if(!this.el){
42316             var cfg = this.getAutoCreate();
42317             this.el = ct.createChild(cfg, position);
42318         }
42319         
42320         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42321         
42322         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42323             this.cb.renderer(this.data) :
42324             String.format('{0}',this.data[this.displayField]);
42325         
42326             
42327         this.el.child('div').dom.setAttribute('qtip',
42328                         String.format('{0}',this.data[this.tipField])
42329         );
42330         
42331         this.el.child('img').on('click', this.remove, this);
42332         
42333     },
42334    
42335     remove : function()
42336     {
42337         if(this.cb.disabled){
42338             return;
42339         }
42340         
42341         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42342             this.cb.items.remove(this);
42343             this.el.child('img').un('click', this.remove, this);
42344             this.el.remove();
42345             this.cb.updateHiddenEl();
42346
42347             this.cb.fireEvent('remove', this.cb, this);
42348         }
42349         
42350     }
42351 });/*
42352  * Based on:
42353  * Ext JS Library 1.1.1
42354  * Copyright(c) 2006-2007, Ext JS, LLC.
42355  *
42356  * Originally Released Under LGPL - original licence link has changed is not relivant.
42357  *
42358  * Fork - LGPL
42359  * <script type="text/javascript">
42360  */
42361 /**
42362  * @class Roo.form.Checkbox
42363  * @extends Roo.form.Field
42364  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42365  * @constructor
42366  * Creates a new Checkbox
42367  * @param {Object} config Configuration options
42368  */
42369 Roo.form.Checkbox = function(config){
42370     Roo.form.Checkbox.superclass.constructor.call(this, config);
42371     this.addEvents({
42372         /**
42373          * @event check
42374          * Fires when the checkbox is checked or unchecked.
42375              * @param {Roo.form.Checkbox} this This checkbox
42376              * @param {Boolean} checked The new checked value
42377              */
42378         check : true
42379     });
42380 };
42381
42382 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42383     /**
42384      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42385      */
42386     focusClass : undefined,
42387     /**
42388      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42389      */
42390     fieldClass: "x-form-field",
42391     /**
42392      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42393      */
42394     checked: false,
42395     /**
42396      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42397      * {tag: "input", type: "checkbox", autocomplete: "off"})
42398      */
42399     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42400     /**
42401      * @cfg {String} boxLabel The text that appears beside the checkbox
42402      */
42403     boxLabel : "",
42404     /**
42405      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42406      */  
42407     inputValue : '1',
42408     /**
42409      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42410      */
42411      valueOff: '0', // value when not checked..
42412
42413     actionMode : 'viewEl', 
42414     //
42415     // private
42416     itemCls : 'x-menu-check-item x-form-item',
42417     groupClass : 'x-menu-group-item',
42418     inputType : 'hidden',
42419     
42420     
42421     inSetChecked: false, // check that we are not calling self...
42422     
42423     inputElement: false, // real input element?
42424     basedOn: false, // ????
42425     
42426     isFormField: true, // not sure where this is needed!!!!
42427
42428     onResize : function(){
42429         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42430         if(!this.boxLabel){
42431             this.el.alignTo(this.wrap, 'c-c');
42432         }
42433     },
42434
42435     initEvents : function(){
42436         Roo.form.Checkbox.superclass.initEvents.call(this);
42437         this.el.on("click", this.onClick,  this);
42438         this.el.on("change", this.onClick,  this);
42439     },
42440
42441
42442     getResizeEl : function(){
42443         return this.wrap;
42444     },
42445
42446     getPositionEl : function(){
42447         return this.wrap;
42448     },
42449
42450     // private
42451     onRender : function(ct, position){
42452         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42453         /*
42454         if(this.inputValue !== undefined){
42455             this.el.dom.value = this.inputValue;
42456         }
42457         */
42458         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42459         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42460         var viewEl = this.wrap.createChild({ 
42461             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42462         this.viewEl = viewEl;   
42463         this.wrap.on('click', this.onClick,  this); 
42464         
42465         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42466         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42467         
42468         
42469         
42470         if(this.boxLabel){
42471             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42472         //    viewEl.on('click', this.onClick,  this); 
42473         }
42474         //if(this.checked){
42475             this.setChecked(this.checked);
42476         //}else{
42477             //this.checked = this.el.dom;
42478         //}
42479
42480     },
42481
42482     // private
42483     initValue : Roo.emptyFn,
42484
42485     /**
42486      * Returns the checked state of the checkbox.
42487      * @return {Boolean} True if checked, else false
42488      */
42489     getValue : function(){
42490         if(this.el){
42491             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42492         }
42493         return this.valueOff;
42494         
42495     },
42496
42497         // private
42498     onClick : function(){ 
42499         if (this.disabled) {
42500             return;
42501         }
42502         this.setChecked(!this.checked);
42503
42504         //if(this.el.dom.checked != this.checked){
42505         //    this.setValue(this.el.dom.checked);
42506        // }
42507     },
42508
42509     /**
42510      * Sets the checked state of the checkbox.
42511      * On is always based on a string comparison between inputValue and the param.
42512      * @param {Boolean/String} value - the value to set 
42513      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42514      */
42515     setValue : function(v,suppressEvent){
42516         
42517         
42518         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42519         //if(this.el && this.el.dom){
42520         //    this.el.dom.checked = this.checked;
42521         //    this.el.dom.defaultChecked = this.checked;
42522         //}
42523         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42524         //this.fireEvent("check", this, this.checked);
42525     },
42526     // private..
42527     setChecked : function(state,suppressEvent)
42528     {
42529         if (this.inSetChecked) {
42530             this.checked = state;
42531             return;
42532         }
42533         
42534     
42535         if(this.wrap){
42536             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42537         }
42538         this.checked = state;
42539         if(suppressEvent !== true){
42540             this.fireEvent('check', this, state);
42541         }
42542         this.inSetChecked = true;
42543         this.el.dom.value = state ? this.inputValue : this.valueOff;
42544         this.inSetChecked = false;
42545         
42546     },
42547     // handle setting of hidden value by some other method!!?!?
42548     setFromHidden: function()
42549     {
42550         if(!this.el){
42551             return;
42552         }
42553         //console.log("SET FROM HIDDEN");
42554         //alert('setFrom hidden');
42555         this.setValue(this.el.dom.value);
42556     },
42557     
42558     onDestroy : function()
42559     {
42560         if(this.viewEl){
42561             Roo.get(this.viewEl).remove();
42562         }
42563          
42564         Roo.form.Checkbox.superclass.onDestroy.call(this);
42565     },
42566     
42567     setBoxLabel : function(str)
42568     {
42569         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42570     }
42571
42572 });/*
42573  * Based on:
42574  * Ext JS Library 1.1.1
42575  * Copyright(c) 2006-2007, Ext JS, LLC.
42576  *
42577  * Originally Released Under LGPL - original licence link has changed is not relivant.
42578  *
42579  * Fork - LGPL
42580  * <script type="text/javascript">
42581  */
42582  
42583 /**
42584  * @class Roo.form.Radio
42585  * @extends Roo.form.Checkbox
42586  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42587  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42588  * @constructor
42589  * Creates a new Radio
42590  * @param {Object} config Configuration options
42591  */
42592 Roo.form.Radio = function(){
42593     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42594 };
42595 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42596     inputType: 'radio',
42597
42598     /**
42599      * If this radio is part of a group, it will return the selected value
42600      * @return {String}
42601      */
42602     getGroupValue : function(){
42603         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42604     },
42605     
42606     
42607     onRender : function(ct, position){
42608         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42609         
42610         if(this.inputValue !== undefined){
42611             this.el.dom.value = this.inputValue;
42612         }
42613          
42614         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42615         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42616         //var viewEl = this.wrap.createChild({ 
42617         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42618         //this.viewEl = viewEl;   
42619         //this.wrap.on('click', this.onClick,  this); 
42620         
42621         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42622         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42623         
42624         
42625         
42626         if(this.boxLabel){
42627             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42628         //    viewEl.on('click', this.onClick,  this); 
42629         }
42630          if(this.checked){
42631             this.el.dom.checked =   'checked' ;
42632         }
42633          
42634     } 
42635     
42636     
42637 });//<script type="text/javascript">
42638
42639 /*
42640  * Based  Ext JS Library 1.1.1
42641  * Copyright(c) 2006-2007, Ext JS, LLC.
42642  * LGPL
42643  *
42644  */
42645  
42646 /**
42647  * @class Roo.HtmlEditorCore
42648  * @extends Roo.Component
42649  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42650  *
42651  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42652  */
42653
42654 Roo.HtmlEditorCore = function(config){
42655     
42656     
42657     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42658     
42659     
42660     this.addEvents({
42661         /**
42662          * @event initialize
42663          * Fires when the editor is fully initialized (including the iframe)
42664          * @param {Roo.HtmlEditorCore} this
42665          */
42666         initialize: true,
42667         /**
42668          * @event activate
42669          * Fires when the editor is first receives the focus. Any insertion must wait
42670          * until after this event.
42671          * @param {Roo.HtmlEditorCore} this
42672          */
42673         activate: true,
42674          /**
42675          * @event beforesync
42676          * Fires before the textarea is updated with content from the editor iframe. Return false
42677          * to cancel the sync.
42678          * @param {Roo.HtmlEditorCore} this
42679          * @param {String} html
42680          */
42681         beforesync: true,
42682          /**
42683          * @event beforepush
42684          * Fires before the iframe editor is updated with content from the textarea. Return false
42685          * to cancel the push.
42686          * @param {Roo.HtmlEditorCore} this
42687          * @param {String} html
42688          */
42689         beforepush: true,
42690          /**
42691          * @event sync
42692          * Fires when the textarea is updated with content from the editor iframe.
42693          * @param {Roo.HtmlEditorCore} this
42694          * @param {String} html
42695          */
42696         sync: true,
42697          /**
42698          * @event push
42699          * Fires when the iframe editor is updated with content from the textarea.
42700          * @param {Roo.HtmlEditorCore} this
42701          * @param {String} html
42702          */
42703         push: true,
42704         
42705         /**
42706          * @event editorevent
42707          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42708          * @param {Roo.HtmlEditorCore} this
42709          */
42710         editorevent: true
42711         
42712     });
42713     
42714     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42715     
42716     // defaults : white / black...
42717     this.applyBlacklists();
42718     
42719     
42720     
42721 };
42722
42723
42724 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42725
42726
42727      /**
42728      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42729      */
42730     
42731     owner : false,
42732     
42733      /**
42734      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42735      *                        Roo.resizable.
42736      */
42737     resizable : false,
42738      /**
42739      * @cfg {Number} height (in pixels)
42740      */   
42741     height: 300,
42742    /**
42743      * @cfg {Number} width (in pixels)
42744      */   
42745     width: 500,
42746     
42747     /**
42748      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42749      * 
42750      */
42751     stylesheets: false,
42752     
42753     // id of frame..
42754     frameId: false,
42755     
42756     // private properties
42757     validationEvent : false,
42758     deferHeight: true,
42759     initialized : false,
42760     activated : false,
42761     sourceEditMode : false,
42762     onFocus : Roo.emptyFn,
42763     iframePad:3,
42764     hideMode:'offsets',
42765     
42766     clearUp: true,
42767     
42768     // blacklist + whitelisted elements..
42769     black: false,
42770     white: false,
42771      
42772     
42773
42774     /**
42775      * Protected method that will not generally be called directly. It
42776      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42777      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42778      */
42779     getDocMarkup : function(){
42780         // body styles..
42781         var st = '';
42782         
42783         // inherit styels from page...?? 
42784         if (this.stylesheets === false) {
42785             
42786             Roo.get(document.head).select('style').each(function(node) {
42787                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42788             });
42789             
42790             Roo.get(document.head).select('link').each(function(node) { 
42791                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42792             });
42793             
42794         } else if (!this.stylesheets.length) {
42795                 // simple..
42796                 st = '<style type="text/css">' +
42797                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42798                    '</style>';
42799         } else { 
42800             
42801         }
42802         
42803         st +=  '<style type="text/css">' +
42804             'IMG { cursor: pointer } ' +
42805         '</style>';
42806
42807         
42808         return '<html><head>' + st  +
42809             //<style type="text/css">' +
42810             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42811             //'</style>' +
42812             ' </head><body class="roo-htmleditor-body"></body></html>';
42813     },
42814
42815     // private
42816     onRender : function(ct, position)
42817     {
42818         var _t = this;
42819         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42820         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42821         
42822         
42823         this.el.dom.style.border = '0 none';
42824         this.el.dom.setAttribute('tabIndex', -1);
42825         this.el.addClass('x-hidden hide');
42826         
42827         
42828         
42829         if(Roo.isIE){ // fix IE 1px bogus margin
42830             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42831         }
42832        
42833         
42834         this.frameId = Roo.id();
42835         
42836          
42837         
42838         var iframe = this.owner.wrap.createChild({
42839             tag: 'iframe',
42840             cls: 'form-control', // bootstrap..
42841             id: this.frameId,
42842             name: this.frameId,
42843             frameBorder : 'no',
42844             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42845         }, this.el
42846         );
42847         
42848         
42849         this.iframe = iframe.dom;
42850
42851          this.assignDocWin();
42852         
42853         this.doc.designMode = 'on';
42854        
42855         this.doc.open();
42856         this.doc.write(this.getDocMarkup());
42857         this.doc.close();
42858
42859         
42860         var task = { // must defer to wait for browser to be ready
42861             run : function(){
42862                 //console.log("run task?" + this.doc.readyState);
42863                 this.assignDocWin();
42864                 if(this.doc.body || this.doc.readyState == 'complete'){
42865                     try {
42866                         this.doc.designMode="on";
42867                     } catch (e) {
42868                         return;
42869                     }
42870                     Roo.TaskMgr.stop(task);
42871                     this.initEditor.defer(10, this);
42872                 }
42873             },
42874             interval : 10,
42875             duration: 10000,
42876             scope: this
42877         };
42878         Roo.TaskMgr.start(task);
42879
42880     },
42881
42882     // private
42883     onResize : function(w, h)
42884     {
42885          Roo.log('resize: ' +w + ',' + h );
42886         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42887         if(!this.iframe){
42888             return;
42889         }
42890         if(typeof w == 'number'){
42891             
42892             this.iframe.style.width = w + 'px';
42893         }
42894         if(typeof h == 'number'){
42895             
42896             this.iframe.style.height = h + 'px';
42897             if(this.doc){
42898                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42899             }
42900         }
42901         
42902     },
42903
42904     /**
42905      * Toggles the editor between standard and source edit mode.
42906      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42907      */
42908     toggleSourceEdit : function(sourceEditMode){
42909         
42910         this.sourceEditMode = sourceEditMode === true;
42911         
42912         if(this.sourceEditMode){
42913  
42914             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42915             
42916         }else{
42917             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42918             //this.iframe.className = '';
42919             this.deferFocus();
42920         }
42921         //this.setSize(this.owner.wrap.getSize());
42922         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42923     },
42924
42925     
42926   
42927
42928     /**
42929      * Protected method that will not generally be called directly. If you need/want
42930      * custom HTML cleanup, this is the method you should override.
42931      * @param {String} html The HTML to be cleaned
42932      * return {String} The cleaned HTML
42933      */
42934     cleanHtml : function(html){
42935         html = String(html);
42936         if(html.length > 5){
42937             if(Roo.isSafari){ // strip safari nonsense
42938                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42939             }
42940         }
42941         if(html == '&nbsp;'){
42942             html = '';
42943         }
42944         return html;
42945     },
42946
42947     /**
42948      * HTML Editor -> Textarea
42949      * Protected method that will not generally be called directly. Syncs the contents
42950      * of the editor iframe with the textarea.
42951      */
42952     syncValue : function(){
42953         if(this.initialized){
42954             var bd = (this.doc.body || this.doc.documentElement);
42955             //this.cleanUpPaste(); -- this is done else where and causes havoc..
42956             var html = bd.innerHTML;
42957             if(Roo.isSafari){
42958                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
42959                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
42960                 if(m && m[1]){
42961                     html = '<div style="'+m[0]+'">' + html + '</div>';
42962                 }
42963             }
42964             html = this.cleanHtml(html);
42965             // fix up the special chars.. normaly like back quotes in word...
42966             // however we do not want to do this with chinese..
42967             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
42968                 var cc = b.charCodeAt();
42969                 if (
42970                     (cc >= 0x4E00 && cc < 0xA000 ) ||
42971                     (cc >= 0x3400 && cc < 0x4E00 ) ||
42972                     (cc >= 0xf900 && cc < 0xfb00 )
42973                 ) {
42974                         return b;
42975                 }
42976                 return "&#"+cc+";" 
42977             });
42978             if(this.owner.fireEvent('beforesync', this, html) !== false){
42979                 this.el.dom.value = html;
42980                 this.owner.fireEvent('sync', this, html);
42981             }
42982         }
42983     },
42984
42985     /**
42986      * Protected method that will not generally be called directly. Pushes the value of the textarea
42987      * into the iframe editor.
42988      */
42989     pushValue : function(){
42990         if(this.initialized){
42991             var v = this.el.dom.value.trim();
42992             
42993 //            if(v.length < 1){
42994 //                v = '&#160;';
42995 //            }
42996             
42997             if(this.owner.fireEvent('beforepush', this, v) !== false){
42998                 var d = (this.doc.body || this.doc.documentElement);
42999                 d.innerHTML = v;
43000                 this.cleanUpPaste();
43001                 this.el.dom.value = d.innerHTML;
43002                 this.owner.fireEvent('push', this, v);
43003             }
43004         }
43005     },
43006
43007     // private
43008     deferFocus : function(){
43009         this.focus.defer(10, this);
43010     },
43011
43012     // doc'ed in Field
43013     focus : function(){
43014         if(this.win && !this.sourceEditMode){
43015             this.win.focus();
43016         }else{
43017             this.el.focus();
43018         }
43019     },
43020     
43021     assignDocWin: function()
43022     {
43023         var iframe = this.iframe;
43024         
43025          if(Roo.isIE){
43026             this.doc = iframe.contentWindow.document;
43027             this.win = iframe.contentWindow;
43028         } else {
43029 //            if (!Roo.get(this.frameId)) {
43030 //                return;
43031 //            }
43032 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43033 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43034             
43035             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43036                 return;
43037             }
43038             
43039             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43040             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43041         }
43042     },
43043     
43044     // private
43045     initEditor : function(){
43046         //console.log("INIT EDITOR");
43047         this.assignDocWin();
43048         
43049         
43050         
43051         this.doc.designMode="on";
43052         this.doc.open();
43053         this.doc.write(this.getDocMarkup());
43054         this.doc.close();
43055         
43056         var dbody = (this.doc.body || this.doc.documentElement);
43057         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43058         // this copies styles from the containing element into thsi one..
43059         // not sure why we need all of this..
43060         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43061         
43062         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43063         //ss['background-attachment'] = 'fixed'; // w3c
43064         dbody.bgProperties = 'fixed'; // ie
43065         //Roo.DomHelper.applyStyles(dbody, ss);
43066         Roo.EventManager.on(this.doc, {
43067             //'mousedown': this.onEditorEvent,
43068             'mouseup': this.onEditorEvent,
43069             'dblclick': this.onEditorEvent,
43070             'click': this.onEditorEvent,
43071             'keyup': this.onEditorEvent,
43072             buffer:100,
43073             scope: this
43074         });
43075         if(Roo.isGecko){
43076             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43077         }
43078         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43079             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43080         }
43081         this.initialized = true;
43082
43083         this.owner.fireEvent('initialize', this);
43084         this.pushValue();
43085     },
43086
43087     // private
43088     onDestroy : function(){
43089         
43090         
43091         
43092         if(this.rendered){
43093             
43094             //for (var i =0; i < this.toolbars.length;i++) {
43095             //    // fixme - ask toolbars for heights?
43096             //    this.toolbars[i].onDestroy();
43097            // }
43098             
43099             //this.wrap.dom.innerHTML = '';
43100             //this.wrap.remove();
43101         }
43102     },
43103
43104     // private
43105     onFirstFocus : function(){
43106         
43107         this.assignDocWin();
43108         
43109         
43110         this.activated = true;
43111          
43112     
43113         if(Roo.isGecko){ // prevent silly gecko errors
43114             this.win.focus();
43115             var s = this.win.getSelection();
43116             if(!s.focusNode || s.focusNode.nodeType != 3){
43117                 var r = s.getRangeAt(0);
43118                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43119                 r.collapse(true);
43120                 this.deferFocus();
43121             }
43122             try{
43123                 this.execCmd('useCSS', true);
43124                 this.execCmd('styleWithCSS', false);
43125             }catch(e){}
43126         }
43127         this.owner.fireEvent('activate', this);
43128     },
43129
43130     // private
43131     adjustFont: function(btn){
43132         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43133         //if(Roo.isSafari){ // safari
43134         //    adjust *= 2;
43135        // }
43136         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43137         if(Roo.isSafari){ // safari
43138             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43139             v =  (v < 10) ? 10 : v;
43140             v =  (v > 48) ? 48 : v;
43141             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43142             
43143         }
43144         
43145         
43146         v = Math.max(1, v+adjust);
43147         
43148         this.execCmd('FontSize', v  );
43149     },
43150
43151     onEditorEvent : function(e)
43152     {
43153         this.owner.fireEvent('editorevent', this, e);
43154       //  this.updateToolbar();
43155         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43156     },
43157
43158     insertTag : function(tg)
43159     {
43160         // could be a bit smarter... -> wrap the current selected tRoo..
43161         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43162             
43163             range = this.createRange(this.getSelection());
43164             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43165             wrappingNode.appendChild(range.extractContents());
43166             range.insertNode(wrappingNode);
43167
43168             return;
43169             
43170             
43171             
43172         }
43173         this.execCmd("formatblock",   tg);
43174         
43175     },
43176     
43177     insertText : function(txt)
43178     {
43179         
43180         
43181         var range = this.createRange();
43182         range.deleteContents();
43183                //alert(Sender.getAttribute('label'));
43184                
43185         range.insertNode(this.doc.createTextNode(txt));
43186     } ,
43187     
43188      
43189
43190     /**
43191      * Executes a Midas editor command on the editor document and performs necessary focus and
43192      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43193      * @param {String} cmd The Midas command
43194      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43195      */
43196     relayCmd : function(cmd, value){
43197         this.win.focus();
43198         this.execCmd(cmd, value);
43199         this.owner.fireEvent('editorevent', this);
43200         //this.updateToolbar();
43201         this.owner.deferFocus();
43202     },
43203
43204     /**
43205      * Executes a Midas editor command directly on the editor document.
43206      * For visual commands, you should use {@link #relayCmd} instead.
43207      * <b>This should only be called after the editor is initialized.</b>
43208      * @param {String} cmd The Midas command
43209      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43210      */
43211     execCmd : function(cmd, value){
43212         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43213         this.syncValue();
43214     },
43215  
43216  
43217    
43218     /**
43219      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43220      * to insert tRoo.
43221      * @param {String} text | dom node.. 
43222      */
43223     insertAtCursor : function(text)
43224     {
43225         
43226         
43227         
43228         if(!this.activated){
43229             return;
43230         }
43231         /*
43232         if(Roo.isIE){
43233             this.win.focus();
43234             var r = this.doc.selection.createRange();
43235             if(r){
43236                 r.collapse(true);
43237                 r.pasteHTML(text);
43238                 this.syncValue();
43239                 this.deferFocus();
43240             
43241             }
43242             return;
43243         }
43244         */
43245         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43246             this.win.focus();
43247             
43248             
43249             // from jquery ui (MIT licenced)
43250             var range, node;
43251             var win = this.win;
43252             
43253             if (win.getSelection && win.getSelection().getRangeAt) {
43254                 range = win.getSelection().getRangeAt(0);
43255                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43256                 range.insertNode(node);
43257             } else if (win.document.selection && win.document.selection.createRange) {
43258                 // no firefox support
43259                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43260                 win.document.selection.createRange().pasteHTML(txt);
43261             } else {
43262                 // no firefox support
43263                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43264                 this.execCmd('InsertHTML', txt);
43265             } 
43266             
43267             this.syncValue();
43268             
43269             this.deferFocus();
43270         }
43271     },
43272  // private
43273     mozKeyPress : function(e){
43274         if(e.ctrlKey){
43275             var c = e.getCharCode(), cmd;
43276           
43277             if(c > 0){
43278                 c = String.fromCharCode(c).toLowerCase();
43279                 switch(c){
43280                     case 'b':
43281                         cmd = 'bold';
43282                         break;
43283                     case 'i':
43284                         cmd = 'italic';
43285                         break;
43286                     
43287                     case 'u':
43288                         cmd = 'underline';
43289                         break;
43290                     
43291                     case 'v':
43292                         this.cleanUpPaste.defer(100, this);
43293                         return;
43294                         
43295                 }
43296                 if(cmd){
43297                     this.win.focus();
43298                     this.execCmd(cmd);
43299                     this.deferFocus();
43300                     e.preventDefault();
43301                 }
43302                 
43303             }
43304         }
43305     },
43306
43307     // private
43308     fixKeys : function(){ // load time branching for fastest keydown performance
43309         if(Roo.isIE){
43310             return function(e){
43311                 var k = e.getKey(), r;
43312                 if(k == e.TAB){
43313                     e.stopEvent();
43314                     r = this.doc.selection.createRange();
43315                     if(r){
43316                         r.collapse(true);
43317                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43318                         this.deferFocus();
43319                     }
43320                     return;
43321                 }
43322                 
43323                 if(k == e.ENTER){
43324                     r = this.doc.selection.createRange();
43325                     if(r){
43326                         var target = r.parentElement();
43327                         if(!target || target.tagName.toLowerCase() != 'li'){
43328                             e.stopEvent();
43329                             r.pasteHTML('<br />');
43330                             r.collapse(false);
43331                             r.select();
43332                         }
43333                     }
43334                 }
43335                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43336                     this.cleanUpPaste.defer(100, this);
43337                     return;
43338                 }
43339                 
43340                 
43341             };
43342         }else if(Roo.isOpera){
43343             return function(e){
43344                 var k = e.getKey();
43345                 if(k == e.TAB){
43346                     e.stopEvent();
43347                     this.win.focus();
43348                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43349                     this.deferFocus();
43350                 }
43351                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43352                     this.cleanUpPaste.defer(100, this);
43353                     return;
43354                 }
43355                 
43356             };
43357         }else if(Roo.isSafari){
43358             return function(e){
43359                 var k = e.getKey();
43360                 
43361                 if(k == e.TAB){
43362                     e.stopEvent();
43363                     this.execCmd('InsertText','\t');
43364                     this.deferFocus();
43365                     return;
43366                 }
43367                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43368                     this.cleanUpPaste.defer(100, this);
43369                     return;
43370                 }
43371                 
43372              };
43373         }
43374     }(),
43375     
43376     getAllAncestors: function()
43377     {
43378         var p = this.getSelectedNode();
43379         var a = [];
43380         if (!p) {
43381             a.push(p); // push blank onto stack..
43382             p = this.getParentElement();
43383         }
43384         
43385         
43386         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43387             a.push(p);
43388             p = p.parentNode;
43389         }
43390         a.push(this.doc.body);
43391         return a;
43392     },
43393     lastSel : false,
43394     lastSelNode : false,
43395     
43396     
43397     getSelection : function() 
43398     {
43399         this.assignDocWin();
43400         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43401     },
43402     
43403     getSelectedNode: function() 
43404     {
43405         // this may only work on Gecko!!!
43406         
43407         // should we cache this!!!!
43408         
43409         
43410         
43411          
43412         var range = this.createRange(this.getSelection()).cloneRange();
43413         
43414         if (Roo.isIE) {
43415             var parent = range.parentElement();
43416             while (true) {
43417                 var testRange = range.duplicate();
43418                 testRange.moveToElementText(parent);
43419                 if (testRange.inRange(range)) {
43420                     break;
43421                 }
43422                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43423                     break;
43424                 }
43425                 parent = parent.parentElement;
43426             }
43427             return parent;
43428         }
43429         
43430         // is ancestor a text element.
43431         var ac =  range.commonAncestorContainer;
43432         if (ac.nodeType == 3) {
43433             ac = ac.parentNode;
43434         }
43435         
43436         var ar = ac.childNodes;
43437          
43438         var nodes = [];
43439         var other_nodes = [];
43440         var has_other_nodes = false;
43441         for (var i=0;i<ar.length;i++) {
43442             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43443                 continue;
43444             }
43445             // fullly contained node.
43446             
43447             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43448                 nodes.push(ar[i]);
43449                 continue;
43450             }
43451             
43452             // probably selected..
43453             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43454                 other_nodes.push(ar[i]);
43455                 continue;
43456             }
43457             // outer..
43458             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43459                 continue;
43460             }
43461             
43462             
43463             has_other_nodes = true;
43464         }
43465         if (!nodes.length && other_nodes.length) {
43466             nodes= other_nodes;
43467         }
43468         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43469             return false;
43470         }
43471         
43472         return nodes[0];
43473     },
43474     createRange: function(sel)
43475     {
43476         // this has strange effects when using with 
43477         // top toolbar - not sure if it's a great idea.
43478         //this.editor.contentWindow.focus();
43479         if (typeof sel != "undefined") {
43480             try {
43481                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43482             } catch(e) {
43483                 return this.doc.createRange();
43484             }
43485         } else {
43486             return this.doc.createRange();
43487         }
43488     },
43489     getParentElement: function()
43490     {
43491         
43492         this.assignDocWin();
43493         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43494         
43495         var range = this.createRange(sel);
43496          
43497         try {
43498             var p = range.commonAncestorContainer;
43499             while (p.nodeType == 3) { // text node
43500                 p = p.parentNode;
43501             }
43502             return p;
43503         } catch (e) {
43504             return null;
43505         }
43506     
43507     },
43508     /***
43509      *
43510      * Range intersection.. the hard stuff...
43511      *  '-1' = before
43512      *  '0' = hits..
43513      *  '1' = after.
43514      *         [ -- selected range --- ]
43515      *   [fail]                        [fail]
43516      *
43517      *    basically..
43518      *      if end is before start or  hits it. fail.
43519      *      if start is after end or hits it fail.
43520      *
43521      *   if either hits (but other is outside. - then it's not 
43522      *   
43523      *    
43524      **/
43525     
43526     
43527     // @see http://www.thismuchiknow.co.uk/?p=64.
43528     rangeIntersectsNode : function(range, node)
43529     {
43530         var nodeRange = node.ownerDocument.createRange();
43531         try {
43532             nodeRange.selectNode(node);
43533         } catch (e) {
43534             nodeRange.selectNodeContents(node);
43535         }
43536     
43537         var rangeStartRange = range.cloneRange();
43538         rangeStartRange.collapse(true);
43539     
43540         var rangeEndRange = range.cloneRange();
43541         rangeEndRange.collapse(false);
43542     
43543         var nodeStartRange = nodeRange.cloneRange();
43544         nodeStartRange.collapse(true);
43545     
43546         var nodeEndRange = nodeRange.cloneRange();
43547         nodeEndRange.collapse(false);
43548     
43549         return rangeStartRange.compareBoundaryPoints(
43550                  Range.START_TO_START, nodeEndRange) == -1 &&
43551                rangeEndRange.compareBoundaryPoints(
43552                  Range.START_TO_START, nodeStartRange) == 1;
43553         
43554          
43555     },
43556     rangeCompareNode : function(range, node)
43557     {
43558         var nodeRange = node.ownerDocument.createRange();
43559         try {
43560             nodeRange.selectNode(node);
43561         } catch (e) {
43562             nodeRange.selectNodeContents(node);
43563         }
43564         
43565         
43566         range.collapse(true);
43567     
43568         nodeRange.collapse(true);
43569      
43570         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43571         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43572          
43573         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43574         
43575         var nodeIsBefore   =  ss == 1;
43576         var nodeIsAfter    = ee == -1;
43577         
43578         if (nodeIsBefore && nodeIsAfter) {
43579             return 0; // outer
43580         }
43581         if (!nodeIsBefore && nodeIsAfter) {
43582             return 1; //right trailed.
43583         }
43584         
43585         if (nodeIsBefore && !nodeIsAfter) {
43586             return 2;  // left trailed.
43587         }
43588         // fully contined.
43589         return 3;
43590     },
43591
43592     // private? - in a new class?
43593     cleanUpPaste :  function()
43594     {
43595         // cleans up the whole document..
43596         Roo.log('cleanuppaste');
43597         
43598         this.cleanUpChildren(this.doc.body);
43599         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43600         if (clean != this.doc.body.innerHTML) {
43601             this.doc.body.innerHTML = clean;
43602         }
43603         
43604     },
43605     
43606     cleanWordChars : function(input) {// change the chars to hex code
43607         var he = Roo.HtmlEditorCore;
43608         
43609         var output = input;
43610         Roo.each(he.swapCodes, function(sw) { 
43611             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43612             
43613             output = output.replace(swapper, sw[1]);
43614         });
43615         
43616         return output;
43617     },
43618     
43619     
43620     cleanUpChildren : function (n)
43621     {
43622         if (!n.childNodes.length) {
43623             return;
43624         }
43625         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43626            this.cleanUpChild(n.childNodes[i]);
43627         }
43628     },
43629     
43630     
43631         
43632     
43633     cleanUpChild : function (node)
43634     {
43635         var ed = this;
43636         //console.log(node);
43637         if (node.nodeName == "#text") {
43638             // clean up silly Windows -- stuff?
43639             return; 
43640         }
43641         if (node.nodeName == "#comment") {
43642             node.parentNode.removeChild(node);
43643             // clean up silly Windows -- stuff?
43644             return; 
43645         }
43646         var lcname = node.tagName.toLowerCase();
43647         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43648         // whitelist of tags..
43649         
43650         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43651             // remove node.
43652             node.parentNode.removeChild(node);
43653             return;
43654             
43655         }
43656         
43657         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43658         
43659         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43660         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43661         
43662         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43663         //    remove_keep_children = true;
43664         //}
43665         
43666         if (remove_keep_children) {
43667             this.cleanUpChildren(node);
43668             // inserts everything just before this node...
43669             while (node.childNodes.length) {
43670                 var cn = node.childNodes[0];
43671                 node.removeChild(cn);
43672                 node.parentNode.insertBefore(cn, node);
43673             }
43674             node.parentNode.removeChild(node);
43675             return;
43676         }
43677         
43678         if (!node.attributes || !node.attributes.length) {
43679             this.cleanUpChildren(node);
43680             return;
43681         }
43682         
43683         function cleanAttr(n,v)
43684         {
43685             
43686             if (v.match(/^\./) || v.match(/^\//)) {
43687                 return;
43688             }
43689             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43690                 return;
43691             }
43692             if (v.match(/^#/)) {
43693                 return;
43694             }
43695 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43696             node.removeAttribute(n);
43697             
43698         }
43699         
43700         var cwhite = this.cwhite;
43701         var cblack = this.cblack;
43702             
43703         function cleanStyle(n,v)
43704         {
43705             if (v.match(/expression/)) { //XSS?? should we even bother..
43706                 node.removeAttribute(n);
43707                 return;
43708             }
43709             
43710             var parts = v.split(/;/);
43711             var clean = [];
43712             
43713             Roo.each(parts, function(p) {
43714                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43715                 if (!p.length) {
43716                     return true;
43717                 }
43718                 var l = p.split(':').shift().replace(/\s+/g,'');
43719                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43720                 
43721                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43722 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43723                     //node.removeAttribute(n);
43724                     return true;
43725                 }
43726                 //Roo.log()
43727                 // only allow 'c whitelisted system attributes'
43728                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43729 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43730                     //node.removeAttribute(n);
43731                     return true;
43732                 }
43733                 
43734                 
43735                  
43736                 
43737                 clean.push(p);
43738                 return true;
43739             });
43740             if (clean.length) { 
43741                 node.setAttribute(n, clean.join(';'));
43742             } else {
43743                 node.removeAttribute(n);
43744             }
43745             
43746         }
43747         
43748         
43749         for (var i = node.attributes.length-1; i > -1 ; i--) {
43750             var a = node.attributes[i];
43751             //console.log(a);
43752             
43753             if (a.name.toLowerCase().substr(0,2)=='on')  {
43754                 node.removeAttribute(a.name);
43755                 continue;
43756             }
43757             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43758                 node.removeAttribute(a.name);
43759                 continue;
43760             }
43761             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43762                 cleanAttr(a.name,a.value); // fixme..
43763                 continue;
43764             }
43765             if (a.name == 'style') {
43766                 cleanStyle(a.name,a.value);
43767                 continue;
43768             }
43769             /// clean up MS crap..
43770             // tecnically this should be a list of valid class'es..
43771             
43772             
43773             if (a.name == 'class') {
43774                 if (a.value.match(/^Mso/)) {
43775                     node.className = '';
43776                 }
43777                 
43778                 if (a.value.match(/body/)) {
43779                     node.className = '';
43780                 }
43781                 continue;
43782             }
43783             
43784             // style cleanup!?
43785             // class cleanup?
43786             
43787         }
43788         
43789         
43790         this.cleanUpChildren(node);
43791         
43792         
43793     },
43794     
43795     /**
43796      * Clean up MS wordisms...
43797      */
43798     cleanWord : function(node)
43799     {
43800         
43801         
43802         if (!node) {
43803             this.cleanWord(this.doc.body);
43804             return;
43805         }
43806         if (node.nodeName == "#text") {
43807             // clean up silly Windows -- stuff?
43808             return; 
43809         }
43810         if (node.nodeName == "#comment") {
43811             node.parentNode.removeChild(node);
43812             // clean up silly Windows -- stuff?
43813             return; 
43814         }
43815         
43816         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43817             node.parentNode.removeChild(node);
43818             return;
43819         }
43820         
43821         // remove - but keep children..
43822         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43823             while (node.childNodes.length) {
43824                 var cn = node.childNodes[0];
43825                 node.removeChild(cn);
43826                 node.parentNode.insertBefore(cn, node);
43827             }
43828             node.parentNode.removeChild(node);
43829             this.iterateChildren(node, this.cleanWord);
43830             return;
43831         }
43832         // clean styles
43833         if (node.className.length) {
43834             
43835             var cn = node.className.split(/\W+/);
43836             var cna = [];
43837             Roo.each(cn, function(cls) {
43838                 if (cls.match(/Mso[a-zA-Z]+/)) {
43839                     return;
43840                 }
43841                 cna.push(cls);
43842             });
43843             node.className = cna.length ? cna.join(' ') : '';
43844             if (!cna.length) {
43845                 node.removeAttribute("class");
43846             }
43847         }
43848         
43849         if (node.hasAttribute("lang")) {
43850             node.removeAttribute("lang");
43851         }
43852         
43853         if (node.hasAttribute("style")) {
43854             
43855             var styles = node.getAttribute("style").split(";");
43856             var nstyle = [];
43857             Roo.each(styles, function(s) {
43858                 if (!s.match(/:/)) {
43859                     return;
43860                 }
43861                 var kv = s.split(":");
43862                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43863                     return;
43864                 }
43865                 // what ever is left... we allow.
43866                 nstyle.push(s);
43867             });
43868             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43869             if (!nstyle.length) {
43870                 node.removeAttribute('style');
43871             }
43872         }
43873         this.iterateChildren(node, this.cleanWord);
43874         
43875         
43876         
43877     },
43878     /**
43879      * iterateChildren of a Node, calling fn each time, using this as the scole..
43880      * @param {DomNode} node node to iterate children of.
43881      * @param {Function} fn method of this class to call on each item.
43882      */
43883     iterateChildren : function(node, fn)
43884     {
43885         if (!node.childNodes.length) {
43886                 return;
43887         }
43888         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43889            fn.call(this, node.childNodes[i])
43890         }
43891     },
43892     
43893     
43894     /**
43895      * cleanTableWidths.
43896      *
43897      * Quite often pasting from word etc.. results in tables with column and widths.
43898      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43899      *
43900      */
43901     cleanTableWidths : function(node)
43902     {
43903          
43904          
43905         if (!node) {
43906             this.cleanTableWidths(this.doc.body);
43907             return;
43908         }
43909         
43910         // ignore list...
43911         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43912             return; 
43913         }
43914         Roo.log(node.tagName);
43915         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43916             this.iterateChildren(node, this.cleanTableWidths);
43917             return;
43918         }
43919         if (node.hasAttribute('width')) {
43920             node.removeAttribute('width');
43921         }
43922         
43923          
43924         if (node.hasAttribute("style")) {
43925             // pretty basic...
43926             
43927             var styles = node.getAttribute("style").split(";");
43928             var nstyle = [];
43929             Roo.each(styles, function(s) {
43930                 if (!s.match(/:/)) {
43931                     return;
43932                 }
43933                 var kv = s.split(":");
43934                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43935                     return;
43936                 }
43937                 // what ever is left... we allow.
43938                 nstyle.push(s);
43939             });
43940             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43941             if (!nstyle.length) {
43942                 node.removeAttribute('style');
43943             }
43944         }
43945         
43946         this.iterateChildren(node, this.cleanTableWidths);
43947         
43948         
43949     },
43950     
43951     
43952     
43953     
43954     domToHTML : function(currentElement, depth, nopadtext) {
43955         
43956         depth = depth || 0;
43957         nopadtext = nopadtext || false;
43958     
43959         if (!currentElement) {
43960             return this.domToHTML(this.doc.body);
43961         }
43962         
43963         //Roo.log(currentElement);
43964         var j;
43965         var allText = false;
43966         var nodeName = currentElement.nodeName;
43967         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
43968         
43969         if  (nodeName == '#text') {
43970             
43971             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
43972         }
43973         
43974         
43975         var ret = '';
43976         if (nodeName != 'BODY') {
43977              
43978             var i = 0;
43979             // Prints the node tagName, such as <A>, <IMG>, etc
43980             if (tagName) {
43981                 var attr = [];
43982                 for(i = 0; i < currentElement.attributes.length;i++) {
43983                     // quoting?
43984                     var aname = currentElement.attributes.item(i).name;
43985                     if (!currentElement.attributes.item(i).value.length) {
43986                         continue;
43987                     }
43988                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
43989                 }
43990                 
43991                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
43992             } 
43993             else {
43994                 
43995                 // eack
43996             }
43997         } else {
43998             tagName = false;
43999         }
44000         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44001             return ret;
44002         }
44003         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44004             nopadtext = true;
44005         }
44006         
44007         
44008         // Traverse the tree
44009         i = 0;
44010         var currentElementChild = currentElement.childNodes.item(i);
44011         var allText = true;
44012         var innerHTML  = '';
44013         lastnode = '';
44014         while (currentElementChild) {
44015             // Formatting code (indent the tree so it looks nice on the screen)
44016             var nopad = nopadtext;
44017             if (lastnode == 'SPAN') {
44018                 nopad  = true;
44019             }
44020             // text
44021             if  (currentElementChild.nodeName == '#text') {
44022                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44023                 toadd = nopadtext ? toadd : toadd.trim();
44024                 if (!nopad && toadd.length > 80) {
44025                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44026                 }
44027                 innerHTML  += toadd;
44028                 
44029                 i++;
44030                 currentElementChild = currentElement.childNodes.item(i);
44031                 lastNode = '';
44032                 continue;
44033             }
44034             allText = false;
44035             
44036             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44037                 
44038             // Recursively traverse the tree structure of the child node
44039             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44040             lastnode = currentElementChild.nodeName;
44041             i++;
44042             currentElementChild=currentElement.childNodes.item(i);
44043         }
44044         
44045         ret += innerHTML;
44046         
44047         if (!allText) {
44048                 // The remaining code is mostly for formatting the tree
44049             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44050         }
44051         
44052         
44053         if (tagName) {
44054             ret+= "</"+tagName+">";
44055         }
44056         return ret;
44057         
44058     },
44059         
44060     applyBlacklists : function()
44061     {
44062         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44063         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44064         
44065         this.white = [];
44066         this.black = [];
44067         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44068             if (b.indexOf(tag) > -1) {
44069                 return;
44070             }
44071             this.white.push(tag);
44072             
44073         }, this);
44074         
44075         Roo.each(w, function(tag) {
44076             if (b.indexOf(tag) > -1) {
44077                 return;
44078             }
44079             if (this.white.indexOf(tag) > -1) {
44080                 return;
44081             }
44082             this.white.push(tag);
44083             
44084         }, this);
44085         
44086         
44087         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44088             if (w.indexOf(tag) > -1) {
44089                 return;
44090             }
44091             this.black.push(tag);
44092             
44093         }, this);
44094         
44095         Roo.each(b, function(tag) {
44096             if (w.indexOf(tag) > -1) {
44097                 return;
44098             }
44099             if (this.black.indexOf(tag) > -1) {
44100                 return;
44101             }
44102             this.black.push(tag);
44103             
44104         }, this);
44105         
44106         
44107         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44108         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44109         
44110         this.cwhite = [];
44111         this.cblack = [];
44112         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44113             if (b.indexOf(tag) > -1) {
44114                 return;
44115             }
44116             this.cwhite.push(tag);
44117             
44118         }, this);
44119         
44120         Roo.each(w, function(tag) {
44121             if (b.indexOf(tag) > -1) {
44122                 return;
44123             }
44124             if (this.cwhite.indexOf(tag) > -1) {
44125                 return;
44126             }
44127             this.cwhite.push(tag);
44128             
44129         }, this);
44130         
44131         
44132         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44133             if (w.indexOf(tag) > -1) {
44134                 return;
44135             }
44136             this.cblack.push(tag);
44137             
44138         }, this);
44139         
44140         Roo.each(b, function(tag) {
44141             if (w.indexOf(tag) > -1) {
44142                 return;
44143             }
44144             if (this.cblack.indexOf(tag) > -1) {
44145                 return;
44146             }
44147             this.cblack.push(tag);
44148             
44149         }, this);
44150     },
44151     
44152     setStylesheets : function(stylesheets)
44153     {
44154         if(typeof(stylesheets) == 'string'){
44155             Roo.get(this.iframe.contentDocument.head).createChild({
44156                 tag : 'link',
44157                 rel : 'stylesheet',
44158                 type : 'text/css',
44159                 href : stylesheets
44160             });
44161             
44162             return;
44163         }
44164         var _this = this;
44165      
44166         Roo.each(stylesheets, function(s) {
44167             if(!s.length){
44168                 return;
44169             }
44170             
44171             Roo.get(_this.iframe.contentDocument.head).createChild({
44172                 tag : 'link',
44173                 rel : 'stylesheet',
44174                 type : 'text/css',
44175                 href : s
44176             });
44177         });
44178
44179         
44180     },
44181     
44182     removeStylesheets : function()
44183     {
44184         var _this = this;
44185         
44186         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44187             s.remove();
44188         });
44189     }
44190     
44191     // hide stuff that is not compatible
44192     /**
44193      * @event blur
44194      * @hide
44195      */
44196     /**
44197      * @event change
44198      * @hide
44199      */
44200     /**
44201      * @event focus
44202      * @hide
44203      */
44204     /**
44205      * @event specialkey
44206      * @hide
44207      */
44208     /**
44209      * @cfg {String} fieldClass @hide
44210      */
44211     /**
44212      * @cfg {String} focusClass @hide
44213      */
44214     /**
44215      * @cfg {String} autoCreate @hide
44216      */
44217     /**
44218      * @cfg {String} inputType @hide
44219      */
44220     /**
44221      * @cfg {String} invalidClass @hide
44222      */
44223     /**
44224      * @cfg {String} invalidText @hide
44225      */
44226     /**
44227      * @cfg {String} msgFx @hide
44228      */
44229     /**
44230      * @cfg {String} validateOnBlur @hide
44231      */
44232 });
44233
44234 Roo.HtmlEditorCore.white = [
44235         'area', 'br', 'img', 'input', 'hr', 'wbr',
44236         
44237        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44238        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44239        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44240        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44241        'table',   'ul',         'xmp', 
44242        
44243        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44244       'thead',   'tr', 
44245      
44246       'dir', 'menu', 'ol', 'ul', 'dl',
44247        
44248       'embed',  'object'
44249 ];
44250
44251
44252 Roo.HtmlEditorCore.black = [
44253     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44254         'applet', // 
44255         'base',   'basefont', 'bgsound', 'blink',  'body', 
44256         'frame',  'frameset', 'head',    'html',   'ilayer', 
44257         'iframe', 'layer',  'link',     'meta',    'object',   
44258         'script', 'style' ,'title',  'xml' // clean later..
44259 ];
44260 Roo.HtmlEditorCore.clean = [
44261     'script', 'style', 'title', 'xml'
44262 ];
44263 Roo.HtmlEditorCore.remove = [
44264     'font'
44265 ];
44266 // attributes..
44267
44268 Roo.HtmlEditorCore.ablack = [
44269     'on'
44270 ];
44271     
44272 Roo.HtmlEditorCore.aclean = [ 
44273     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44274 ];
44275
44276 // protocols..
44277 Roo.HtmlEditorCore.pwhite= [
44278         'http',  'https',  'mailto'
44279 ];
44280
44281 // white listed style attributes.
44282 Roo.HtmlEditorCore.cwhite= [
44283       //  'text-align', /// default is to allow most things..
44284       
44285          
44286 //        'font-size'//??
44287 ];
44288
44289 // black listed style attributes.
44290 Roo.HtmlEditorCore.cblack= [
44291       //  'font-size' -- this can be set by the project 
44292 ];
44293
44294
44295 Roo.HtmlEditorCore.swapCodes   =[ 
44296     [    8211, "--" ], 
44297     [    8212, "--" ], 
44298     [    8216,  "'" ],  
44299     [    8217, "'" ],  
44300     [    8220, '"' ],  
44301     [    8221, '"' ],  
44302     [    8226, "*" ],  
44303     [    8230, "..." ]
44304 ]; 
44305
44306     //<script type="text/javascript">
44307
44308 /*
44309  * Ext JS Library 1.1.1
44310  * Copyright(c) 2006-2007, Ext JS, LLC.
44311  * Licence LGPL
44312  * 
44313  */
44314  
44315  
44316 Roo.form.HtmlEditor = function(config){
44317     
44318     
44319     
44320     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44321     
44322     if (!this.toolbars) {
44323         this.toolbars = [];
44324     }
44325     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44326     
44327     
44328 };
44329
44330 /**
44331  * @class Roo.form.HtmlEditor
44332  * @extends Roo.form.Field
44333  * Provides a lightweight HTML Editor component.
44334  *
44335  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44336  * 
44337  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44338  * supported by this editor.</b><br/><br/>
44339  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44340  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44341  */
44342 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44343     /**
44344      * @cfg {Boolean} clearUp
44345      */
44346     clearUp : true,
44347       /**
44348      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44349      */
44350     toolbars : false,
44351    
44352      /**
44353      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44354      *                        Roo.resizable.
44355      */
44356     resizable : false,
44357      /**
44358      * @cfg {Number} height (in pixels)
44359      */   
44360     height: 300,
44361    /**
44362      * @cfg {Number} width (in pixels)
44363      */   
44364     width: 500,
44365     
44366     /**
44367      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44368      * 
44369      */
44370     stylesheets: false,
44371     
44372     
44373      /**
44374      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44375      * 
44376      */
44377     cblack: false,
44378     /**
44379      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44380      * 
44381      */
44382     cwhite: false,
44383     
44384      /**
44385      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44386      * 
44387      */
44388     black: false,
44389     /**
44390      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44391      * 
44392      */
44393     white: false,
44394     
44395     // id of frame..
44396     frameId: false,
44397     
44398     // private properties
44399     validationEvent : false,
44400     deferHeight: true,
44401     initialized : false,
44402     activated : false,
44403     
44404     onFocus : Roo.emptyFn,
44405     iframePad:3,
44406     hideMode:'offsets',
44407     
44408     actionMode : 'container', // defaults to hiding it...
44409     
44410     defaultAutoCreate : { // modified by initCompnoent..
44411         tag: "textarea",
44412         style:"width:500px;height:300px;",
44413         autocomplete: "new-password"
44414     },
44415
44416     // private
44417     initComponent : function(){
44418         this.addEvents({
44419             /**
44420              * @event initialize
44421              * Fires when the editor is fully initialized (including the iframe)
44422              * @param {HtmlEditor} this
44423              */
44424             initialize: true,
44425             /**
44426              * @event activate
44427              * Fires when the editor is first receives the focus. Any insertion must wait
44428              * until after this event.
44429              * @param {HtmlEditor} this
44430              */
44431             activate: true,
44432              /**
44433              * @event beforesync
44434              * Fires before the textarea is updated with content from the editor iframe. Return false
44435              * to cancel the sync.
44436              * @param {HtmlEditor} this
44437              * @param {String} html
44438              */
44439             beforesync: true,
44440              /**
44441              * @event beforepush
44442              * Fires before the iframe editor is updated with content from the textarea. Return false
44443              * to cancel the push.
44444              * @param {HtmlEditor} this
44445              * @param {String} html
44446              */
44447             beforepush: true,
44448              /**
44449              * @event sync
44450              * Fires when the textarea is updated with content from the editor iframe.
44451              * @param {HtmlEditor} this
44452              * @param {String} html
44453              */
44454             sync: true,
44455              /**
44456              * @event push
44457              * Fires when the iframe editor is updated with content from the textarea.
44458              * @param {HtmlEditor} this
44459              * @param {String} html
44460              */
44461             push: true,
44462              /**
44463              * @event editmodechange
44464              * Fires when the editor switches edit modes
44465              * @param {HtmlEditor} this
44466              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44467              */
44468             editmodechange: true,
44469             /**
44470              * @event editorevent
44471              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44472              * @param {HtmlEditor} this
44473              */
44474             editorevent: true,
44475             /**
44476              * @event firstfocus
44477              * Fires when on first focus - needed by toolbars..
44478              * @param {HtmlEditor} this
44479              */
44480             firstfocus: true,
44481             /**
44482              * @event autosave
44483              * Auto save the htmlEditor value as a file into Events
44484              * @param {HtmlEditor} this
44485              */
44486             autosave: true,
44487             /**
44488              * @event savedpreview
44489              * preview the saved version of htmlEditor
44490              * @param {HtmlEditor} this
44491              */
44492             savedpreview: true,
44493             
44494             /**
44495             * @event stylesheetsclick
44496             * Fires when press the Sytlesheets button
44497             * @param {Roo.HtmlEditorCore} this
44498             */
44499             stylesheetsclick: true
44500         });
44501         this.defaultAutoCreate =  {
44502             tag: "textarea",
44503             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44504             autocomplete: "new-password"
44505         };
44506     },
44507
44508     /**
44509      * Protected method that will not generally be called directly. It
44510      * is called when the editor creates its toolbar. Override this method if you need to
44511      * add custom toolbar buttons.
44512      * @param {HtmlEditor} editor
44513      */
44514     createToolbar : function(editor){
44515         Roo.log("create toolbars");
44516         if (!editor.toolbars || !editor.toolbars.length) {
44517             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44518         }
44519         
44520         for (var i =0 ; i < editor.toolbars.length;i++) {
44521             editor.toolbars[i] = Roo.factory(
44522                     typeof(editor.toolbars[i]) == 'string' ?
44523                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44524                 Roo.form.HtmlEditor);
44525             editor.toolbars[i].init(editor);
44526         }
44527          
44528         
44529     },
44530
44531      
44532     // private
44533     onRender : function(ct, position)
44534     {
44535         var _t = this;
44536         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44537         
44538         this.wrap = this.el.wrap({
44539             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44540         });
44541         
44542         this.editorcore.onRender(ct, position);
44543          
44544         if (this.resizable) {
44545             this.resizeEl = new Roo.Resizable(this.wrap, {
44546                 pinned : true,
44547                 wrap: true,
44548                 dynamic : true,
44549                 minHeight : this.height,
44550                 height: this.height,
44551                 handles : this.resizable,
44552                 width: this.width,
44553                 listeners : {
44554                     resize : function(r, w, h) {
44555                         _t.onResize(w,h); // -something
44556                     }
44557                 }
44558             });
44559             
44560         }
44561         this.createToolbar(this);
44562        
44563         
44564         if(!this.width){
44565             this.setSize(this.wrap.getSize());
44566         }
44567         if (this.resizeEl) {
44568             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44569             // should trigger onReize..
44570         }
44571         
44572         this.keyNav = new Roo.KeyNav(this.el, {
44573             
44574             "tab" : function(e){
44575                 e.preventDefault();
44576                 
44577                 var value = this.getValue();
44578                 
44579                 var start = this.el.dom.selectionStart;
44580                 var end = this.el.dom.selectionEnd;
44581                 
44582                 if(!e.shiftKey){
44583                     
44584                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44585                     this.el.dom.setSelectionRange(end + 1, end + 1);
44586                     return;
44587                 }
44588                 
44589                 var f = value.substring(0, start).split("\t");
44590                 
44591                 if(f.pop().length != 0){
44592                     return;
44593                 }
44594                 
44595                 this.setValue(f.join("\t") + value.substring(end));
44596                 this.el.dom.setSelectionRange(start - 1, start - 1);
44597                 
44598             },
44599             
44600             "home" : function(e){
44601                 e.preventDefault();
44602                 
44603                 var curr = this.el.dom.selectionStart;
44604                 var lines = this.getValue().split("\n");
44605                 
44606                 if(!lines.length){
44607                     return;
44608                 }
44609                 
44610                 if(e.ctrlKey){
44611                     this.el.dom.setSelectionRange(0, 0);
44612                     return;
44613                 }
44614                 
44615                 var pos = 0;
44616                 
44617                 for (var i = 0; i < lines.length;i++) {
44618                     pos += lines[i].length;
44619                     
44620                     if(i != 0){
44621                         pos += 1;
44622                     }
44623                     
44624                     if(pos < curr){
44625                         continue;
44626                     }
44627                     
44628                     pos -= lines[i].length;
44629                     
44630                     break;
44631                 }
44632                 
44633                 if(!e.shiftKey){
44634                     this.el.dom.setSelectionRange(pos, pos);
44635                     return;
44636                 }
44637                 
44638                 this.el.dom.selectionStart = pos;
44639                 this.el.dom.selectionEnd = curr;
44640             },
44641             
44642             "end" : function(e){
44643                 e.preventDefault();
44644                 
44645                 var curr = this.el.dom.selectionStart;
44646                 var lines = this.getValue().split("\n");
44647                 
44648                 if(!lines.length){
44649                     return;
44650                 }
44651                 
44652                 if(e.ctrlKey){
44653                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44654                     return;
44655                 }
44656                 
44657                 var pos = 0;
44658                 
44659                 for (var i = 0; i < lines.length;i++) {
44660                     
44661                     pos += lines[i].length;
44662                     
44663                     if(i != 0){
44664                         pos += 1;
44665                     }
44666                     
44667                     if(pos < curr){
44668                         continue;
44669                     }
44670                     
44671                     break;
44672                 }
44673                 
44674                 if(!e.shiftKey){
44675                     this.el.dom.setSelectionRange(pos, pos);
44676                     return;
44677                 }
44678                 
44679                 this.el.dom.selectionStart = curr;
44680                 this.el.dom.selectionEnd = pos;
44681             },
44682
44683             scope : this,
44684
44685             doRelay : function(foo, bar, hname){
44686                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44687             },
44688
44689             forceKeyDown: true
44690         });
44691         
44692 //        if(this.autosave && this.w){
44693 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44694 //        }
44695     },
44696
44697     // private
44698     onResize : function(w, h)
44699     {
44700         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44701         var ew = false;
44702         var eh = false;
44703         
44704         if(this.el ){
44705             if(typeof w == 'number'){
44706                 var aw = w - this.wrap.getFrameWidth('lr');
44707                 this.el.setWidth(this.adjustWidth('textarea', aw));
44708                 ew = aw;
44709             }
44710             if(typeof h == 'number'){
44711                 var tbh = 0;
44712                 for (var i =0; i < this.toolbars.length;i++) {
44713                     // fixme - ask toolbars for heights?
44714                     tbh += this.toolbars[i].tb.el.getHeight();
44715                     if (this.toolbars[i].footer) {
44716                         tbh += this.toolbars[i].footer.el.getHeight();
44717                     }
44718                 }
44719                 
44720                 
44721                 
44722                 
44723                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44724                 ah -= 5; // knock a few pixes off for look..
44725 //                Roo.log(ah);
44726                 this.el.setHeight(this.adjustWidth('textarea', ah));
44727                 var eh = ah;
44728             }
44729         }
44730         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44731         this.editorcore.onResize(ew,eh);
44732         
44733     },
44734
44735     /**
44736      * Toggles the editor between standard and source edit mode.
44737      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44738      */
44739     toggleSourceEdit : function(sourceEditMode)
44740     {
44741         this.editorcore.toggleSourceEdit(sourceEditMode);
44742         
44743         if(this.editorcore.sourceEditMode){
44744             Roo.log('editor - showing textarea');
44745             
44746 //            Roo.log('in');
44747 //            Roo.log(this.syncValue());
44748             this.editorcore.syncValue();
44749             this.el.removeClass('x-hidden');
44750             this.el.dom.removeAttribute('tabIndex');
44751             this.el.focus();
44752             
44753             for (var i = 0; i < this.toolbars.length; i++) {
44754                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44755                     this.toolbars[i].tb.hide();
44756                     this.toolbars[i].footer.hide();
44757                 }
44758             }
44759             
44760         }else{
44761             Roo.log('editor - hiding textarea');
44762 //            Roo.log('out')
44763 //            Roo.log(this.pushValue()); 
44764             this.editorcore.pushValue();
44765             
44766             this.el.addClass('x-hidden');
44767             this.el.dom.setAttribute('tabIndex', -1);
44768             
44769             for (var i = 0; i < this.toolbars.length; i++) {
44770                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44771                     this.toolbars[i].tb.show();
44772                     this.toolbars[i].footer.show();
44773                 }
44774             }
44775             
44776             //this.deferFocus();
44777         }
44778         
44779         this.setSize(this.wrap.getSize());
44780         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44781         
44782         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44783     },
44784  
44785     // private (for BoxComponent)
44786     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44787
44788     // private (for BoxComponent)
44789     getResizeEl : function(){
44790         return this.wrap;
44791     },
44792
44793     // private (for BoxComponent)
44794     getPositionEl : function(){
44795         return this.wrap;
44796     },
44797
44798     // private
44799     initEvents : function(){
44800         this.originalValue = this.getValue();
44801     },
44802
44803     /**
44804      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44805      * @method
44806      */
44807     markInvalid : Roo.emptyFn,
44808     /**
44809      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44810      * @method
44811      */
44812     clearInvalid : Roo.emptyFn,
44813
44814     setValue : function(v){
44815         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44816         this.editorcore.pushValue();
44817     },
44818
44819      
44820     // private
44821     deferFocus : function(){
44822         this.focus.defer(10, this);
44823     },
44824
44825     // doc'ed in Field
44826     focus : function(){
44827         this.editorcore.focus();
44828         
44829     },
44830       
44831
44832     // private
44833     onDestroy : function(){
44834         
44835         
44836         
44837         if(this.rendered){
44838             
44839             for (var i =0; i < this.toolbars.length;i++) {
44840                 // fixme - ask toolbars for heights?
44841                 this.toolbars[i].onDestroy();
44842             }
44843             
44844             this.wrap.dom.innerHTML = '';
44845             this.wrap.remove();
44846         }
44847     },
44848
44849     // private
44850     onFirstFocus : function(){
44851         //Roo.log("onFirstFocus");
44852         this.editorcore.onFirstFocus();
44853          for (var i =0; i < this.toolbars.length;i++) {
44854             this.toolbars[i].onFirstFocus();
44855         }
44856         
44857     },
44858     
44859     // private
44860     syncValue : function()
44861     {
44862         this.editorcore.syncValue();
44863     },
44864     
44865     pushValue : function()
44866     {
44867         this.editorcore.pushValue();
44868     },
44869     
44870     setStylesheets : function(stylesheets)
44871     {
44872         this.editorcore.setStylesheets(stylesheets);
44873     },
44874     
44875     removeStylesheets : function()
44876     {
44877         this.editorcore.removeStylesheets();
44878     }
44879      
44880     
44881     // hide stuff that is not compatible
44882     /**
44883      * @event blur
44884      * @hide
44885      */
44886     /**
44887      * @event change
44888      * @hide
44889      */
44890     /**
44891      * @event focus
44892      * @hide
44893      */
44894     /**
44895      * @event specialkey
44896      * @hide
44897      */
44898     /**
44899      * @cfg {String} fieldClass @hide
44900      */
44901     /**
44902      * @cfg {String} focusClass @hide
44903      */
44904     /**
44905      * @cfg {String} autoCreate @hide
44906      */
44907     /**
44908      * @cfg {String} inputType @hide
44909      */
44910     /**
44911      * @cfg {String} invalidClass @hide
44912      */
44913     /**
44914      * @cfg {String} invalidText @hide
44915      */
44916     /**
44917      * @cfg {String} msgFx @hide
44918      */
44919     /**
44920      * @cfg {String} validateOnBlur @hide
44921      */
44922 });
44923  
44924     // <script type="text/javascript">
44925 /*
44926  * Based on
44927  * Ext JS Library 1.1.1
44928  * Copyright(c) 2006-2007, Ext JS, LLC.
44929  *  
44930  
44931  */
44932
44933 /**
44934  * @class Roo.form.HtmlEditorToolbar1
44935  * Basic Toolbar
44936  * 
44937  * Usage:
44938  *
44939  new Roo.form.HtmlEditor({
44940     ....
44941     toolbars : [
44942         new Roo.form.HtmlEditorToolbar1({
44943             disable : { fonts: 1 , format: 1, ..., ... , ...],
44944             btns : [ .... ]
44945         })
44946     }
44947      
44948  * 
44949  * @cfg {Object} disable List of elements to disable..
44950  * @cfg {Array} btns List of additional buttons.
44951  * 
44952  * 
44953  * NEEDS Extra CSS? 
44954  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
44955  */
44956  
44957 Roo.form.HtmlEditor.ToolbarStandard = function(config)
44958 {
44959     
44960     Roo.apply(this, config);
44961     
44962     // default disabled, based on 'good practice'..
44963     this.disable = this.disable || {};
44964     Roo.applyIf(this.disable, {
44965         fontSize : true,
44966         colors : true,
44967         specialElements : true
44968     });
44969     
44970     
44971     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44972     // dont call parent... till later.
44973 }
44974
44975 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
44976     
44977     tb: false,
44978     
44979     rendered: false,
44980     
44981     editor : false,
44982     editorcore : false,
44983     /**
44984      * @cfg {Object} disable  List of toolbar elements to disable
44985          
44986      */
44987     disable : false,
44988     
44989     
44990      /**
44991      * @cfg {String} createLinkText The default text for the create link prompt
44992      */
44993     createLinkText : 'Please enter the URL for the link:',
44994     /**
44995      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
44996      */
44997     defaultLinkValue : 'http:/'+'/',
44998    
44999     
45000       /**
45001      * @cfg {Array} fontFamilies An array of available font families
45002      */
45003     fontFamilies : [
45004         'Arial',
45005         'Courier New',
45006         'Tahoma',
45007         'Times New Roman',
45008         'Verdana'
45009     ],
45010     
45011     specialChars : [
45012            "&#169;",
45013           "&#174;",     
45014           "&#8482;",    
45015           "&#163;" ,    
45016          // "&#8212;",    
45017           "&#8230;",    
45018           "&#247;" ,    
45019         //  "&#225;" ,     ?? a acute?
45020            "&#8364;"    , //Euro
45021        //   "&#8220;"    ,
45022         //  "&#8221;"    ,
45023         //  "&#8226;"    ,
45024           "&#176;"  //   , // degrees
45025
45026          // "&#233;"     , // e ecute
45027          // "&#250;"     , // u ecute?
45028     ],
45029     
45030     specialElements : [
45031         {
45032             text: "Insert Table",
45033             xtype: 'MenuItem',
45034             xns : Roo.Menu,
45035             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45036                 
45037         },
45038         {    
45039             text: "Insert Image",
45040             xtype: 'MenuItem',
45041             xns : Roo.Menu,
45042             ihtml : '<img src="about:blank"/>'
45043             
45044         }
45045         
45046          
45047     ],
45048     
45049     
45050     inputElements : [ 
45051             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45052             "input:submit", "input:button", "select", "textarea", "label" ],
45053     formats : [
45054         ["p"] ,  
45055         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45056         ["pre"],[ "code"], 
45057         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45058         ['div'],['span']
45059     ],
45060     
45061     cleanStyles : [
45062         "font-size"
45063     ],
45064      /**
45065      * @cfg {String} defaultFont default font to use.
45066      */
45067     defaultFont: 'tahoma',
45068    
45069     fontSelect : false,
45070     
45071     
45072     formatCombo : false,
45073     
45074     init : function(editor)
45075     {
45076         this.editor = editor;
45077         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45078         var editorcore = this.editorcore;
45079         
45080         var _t = this;
45081         
45082         var fid = editorcore.frameId;
45083         var etb = this;
45084         function btn(id, toggle, handler){
45085             var xid = fid + '-'+ id ;
45086             return {
45087                 id : xid,
45088                 cmd : id,
45089                 cls : 'x-btn-icon x-edit-'+id,
45090                 enableToggle:toggle !== false,
45091                 scope: _t, // was editor...
45092                 handler:handler||_t.relayBtnCmd,
45093                 clickEvent:'mousedown',
45094                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45095                 tabIndex:-1
45096             };
45097         }
45098         
45099         
45100         
45101         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45102         this.tb = tb;
45103          // stop form submits
45104         tb.el.on('click', function(e){
45105             e.preventDefault(); // what does this do?
45106         });
45107
45108         if(!this.disable.font) { // && !Roo.isSafari){
45109             /* why no safari for fonts 
45110             editor.fontSelect = tb.el.createChild({
45111                 tag:'select',
45112                 tabIndex: -1,
45113                 cls:'x-font-select',
45114                 html: this.createFontOptions()
45115             });
45116             
45117             editor.fontSelect.on('change', function(){
45118                 var font = editor.fontSelect.dom.value;
45119                 editor.relayCmd('fontname', font);
45120                 editor.deferFocus();
45121             }, editor);
45122             
45123             tb.add(
45124                 editor.fontSelect.dom,
45125                 '-'
45126             );
45127             */
45128             
45129         };
45130         if(!this.disable.formats){
45131             this.formatCombo = new Roo.form.ComboBox({
45132                 store: new Roo.data.SimpleStore({
45133                     id : 'tag',
45134                     fields: ['tag'],
45135                     data : this.formats // from states.js
45136                 }),
45137                 blockFocus : true,
45138                 name : '',
45139                 //autoCreate : {tag: "div",  size: "20"},
45140                 displayField:'tag',
45141                 typeAhead: false,
45142                 mode: 'local',
45143                 editable : false,
45144                 triggerAction: 'all',
45145                 emptyText:'Add tag',
45146                 selectOnFocus:true,
45147                 width:135,
45148                 listeners : {
45149                     'select': function(c, r, i) {
45150                         editorcore.insertTag(r.get('tag'));
45151                         editor.focus();
45152                     }
45153                 }
45154
45155             });
45156             tb.addField(this.formatCombo);
45157             
45158         }
45159         
45160         if(!this.disable.format){
45161             tb.add(
45162                 btn('bold'),
45163                 btn('italic'),
45164                 btn('underline'),
45165                 btn('strikethrough')
45166             );
45167         };
45168         if(!this.disable.fontSize){
45169             tb.add(
45170                 '-',
45171                 
45172                 
45173                 btn('increasefontsize', false, editorcore.adjustFont),
45174                 btn('decreasefontsize', false, editorcore.adjustFont)
45175             );
45176         };
45177         
45178         
45179         if(!this.disable.colors){
45180             tb.add(
45181                 '-', {
45182                     id:editorcore.frameId +'-forecolor',
45183                     cls:'x-btn-icon x-edit-forecolor',
45184                     clickEvent:'mousedown',
45185                     tooltip: this.buttonTips['forecolor'] || undefined,
45186                     tabIndex:-1,
45187                     menu : new Roo.menu.ColorMenu({
45188                         allowReselect: true,
45189                         focus: Roo.emptyFn,
45190                         value:'000000',
45191                         plain:true,
45192                         selectHandler: function(cp, color){
45193                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45194                             editor.deferFocus();
45195                         },
45196                         scope: editorcore,
45197                         clickEvent:'mousedown'
45198                     })
45199                 }, {
45200                     id:editorcore.frameId +'backcolor',
45201                     cls:'x-btn-icon x-edit-backcolor',
45202                     clickEvent:'mousedown',
45203                     tooltip: this.buttonTips['backcolor'] || undefined,
45204                     tabIndex:-1,
45205                     menu : new Roo.menu.ColorMenu({
45206                         focus: Roo.emptyFn,
45207                         value:'FFFFFF',
45208                         plain:true,
45209                         allowReselect: true,
45210                         selectHandler: function(cp, color){
45211                             if(Roo.isGecko){
45212                                 editorcore.execCmd('useCSS', false);
45213                                 editorcore.execCmd('hilitecolor', color);
45214                                 editorcore.execCmd('useCSS', true);
45215                                 editor.deferFocus();
45216                             }else{
45217                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45218                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45219                                 editor.deferFocus();
45220                             }
45221                         },
45222                         scope:editorcore,
45223                         clickEvent:'mousedown'
45224                     })
45225                 }
45226             );
45227         };
45228         // now add all the items...
45229         
45230
45231         if(!this.disable.alignments){
45232             tb.add(
45233                 '-',
45234                 btn('justifyleft'),
45235                 btn('justifycenter'),
45236                 btn('justifyright')
45237             );
45238         };
45239
45240         //if(!Roo.isSafari){
45241             if(!this.disable.links){
45242                 tb.add(
45243                     '-',
45244                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45245                 );
45246             };
45247
45248             if(!this.disable.lists){
45249                 tb.add(
45250                     '-',
45251                     btn('insertorderedlist'),
45252                     btn('insertunorderedlist')
45253                 );
45254             }
45255             if(!this.disable.sourceEdit){
45256                 tb.add(
45257                     '-',
45258                     btn('sourceedit', true, function(btn){
45259                         this.toggleSourceEdit(btn.pressed);
45260                     })
45261                 );
45262             }
45263         //}
45264         
45265         var smenu = { };
45266         // special menu.. - needs to be tidied up..
45267         if (!this.disable.special) {
45268             smenu = {
45269                 text: "&#169;",
45270                 cls: 'x-edit-none',
45271                 
45272                 menu : {
45273                     items : []
45274                 }
45275             };
45276             for (var i =0; i < this.specialChars.length; i++) {
45277                 smenu.menu.items.push({
45278                     
45279                     html: this.specialChars[i],
45280                     handler: function(a,b) {
45281                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45282                         //editor.insertAtCursor(a.html);
45283                         
45284                     },
45285                     tabIndex:-1
45286                 });
45287             }
45288             
45289             
45290             tb.add(smenu);
45291             
45292             
45293         }
45294         
45295         var cmenu = { };
45296         if (!this.disable.cleanStyles) {
45297             cmenu = {
45298                 cls: 'x-btn-icon x-btn-clear',
45299                 
45300                 menu : {
45301                     items : []
45302                 }
45303             };
45304             for (var i =0; i < this.cleanStyles.length; i++) {
45305                 cmenu.menu.items.push({
45306                     actiontype : this.cleanStyles[i],
45307                     html: 'Remove ' + this.cleanStyles[i],
45308                     handler: function(a,b) {
45309 //                        Roo.log(a);
45310 //                        Roo.log(b);
45311                         var c = Roo.get(editorcore.doc.body);
45312                         c.select('[style]').each(function(s) {
45313                             s.dom.style.removeProperty(a.actiontype);
45314                         });
45315                         editorcore.syncValue();
45316                     },
45317                     tabIndex:-1
45318                 });
45319             }
45320              cmenu.menu.items.push({
45321                 actiontype : 'tablewidths',
45322                 html: 'Remove Table Widths',
45323                 handler: function(a,b) {
45324                     editorcore.cleanTableWidths();
45325                     editorcore.syncValue();
45326                 },
45327                 tabIndex:-1
45328             });
45329             cmenu.menu.items.push({
45330                 actiontype : 'word',
45331                 html: 'Remove MS Word Formating',
45332                 handler: function(a,b) {
45333                     editorcore.cleanWord();
45334                     editorcore.syncValue();
45335                 },
45336                 tabIndex:-1
45337             });
45338             
45339             cmenu.menu.items.push({
45340                 actiontype : 'all',
45341                 html: 'Remove All Styles',
45342                 handler: function(a,b) {
45343                     
45344                     var c = Roo.get(editorcore.doc.body);
45345                     c.select('[style]').each(function(s) {
45346                         s.dom.removeAttribute('style');
45347                     });
45348                     editorcore.syncValue();
45349                 },
45350                 tabIndex:-1
45351             });
45352             
45353             cmenu.menu.items.push({
45354                 actiontype : 'all',
45355                 html: 'Remove All CSS Classes',
45356                 handler: function(a,b) {
45357                     
45358                     var c = Roo.get(editorcore.doc.body);
45359                     c.select('[class]').each(function(s) {
45360                         s.dom.className = '';
45361                     });
45362                     editorcore.syncValue();
45363                 },
45364                 tabIndex:-1
45365             });
45366             
45367              cmenu.menu.items.push({
45368                 actiontype : 'tidy',
45369                 html: 'Tidy HTML Source',
45370                 handler: function(a,b) {
45371                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45372                     editorcore.syncValue();
45373                 },
45374                 tabIndex:-1
45375             });
45376             
45377             
45378             tb.add(cmenu);
45379         }
45380          
45381         if (!this.disable.specialElements) {
45382             var semenu = {
45383                 text: "Other;",
45384                 cls: 'x-edit-none',
45385                 menu : {
45386                     items : []
45387                 }
45388             };
45389             for (var i =0; i < this.specialElements.length; i++) {
45390                 semenu.menu.items.push(
45391                     Roo.apply({ 
45392                         handler: function(a,b) {
45393                             editor.insertAtCursor(this.ihtml);
45394                         }
45395                     }, this.specialElements[i])
45396                 );
45397                     
45398             }
45399             
45400             tb.add(semenu);
45401             
45402             
45403         }
45404          
45405         
45406         if (this.btns) {
45407             for(var i =0; i< this.btns.length;i++) {
45408                 var b = Roo.factory(this.btns[i],Roo.form);
45409                 b.cls =  'x-edit-none';
45410                 
45411                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45412                     b.cls += ' x-init-enable';
45413                 }
45414                 
45415                 b.scope = editorcore;
45416                 tb.add(b);
45417             }
45418         
45419         }
45420         
45421         
45422         
45423         // disable everything...
45424         
45425         this.tb.items.each(function(item){
45426             
45427            if(
45428                 item.id != editorcore.frameId+ '-sourceedit' && 
45429                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45430             ){
45431                 
45432                 item.disable();
45433             }
45434         });
45435         this.rendered = true;
45436         
45437         // the all the btns;
45438         editor.on('editorevent', this.updateToolbar, this);
45439         // other toolbars need to implement this..
45440         //editor.on('editmodechange', this.updateToolbar, this);
45441     },
45442     
45443     
45444     relayBtnCmd : function(btn) {
45445         this.editorcore.relayCmd(btn.cmd);
45446     },
45447     // private used internally
45448     createLink : function(){
45449         Roo.log("create link?");
45450         var url = prompt(this.createLinkText, this.defaultLinkValue);
45451         if(url && url != 'http:/'+'/'){
45452             this.editorcore.relayCmd('createlink', url);
45453         }
45454     },
45455
45456     
45457     /**
45458      * Protected method that will not generally be called directly. It triggers
45459      * a toolbar update by reading the markup state of the current selection in the editor.
45460      */
45461     updateToolbar: function(){
45462
45463         if(!this.editorcore.activated){
45464             this.editor.onFirstFocus();
45465             return;
45466         }
45467
45468         var btns = this.tb.items.map, 
45469             doc = this.editorcore.doc,
45470             frameId = this.editorcore.frameId;
45471
45472         if(!this.disable.font && !Roo.isSafari){
45473             /*
45474             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45475             if(name != this.fontSelect.dom.value){
45476                 this.fontSelect.dom.value = name;
45477             }
45478             */
45479         }
45480         if(!this.disable.format){
45481             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45482             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45483             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45484             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45485         }
45486         if(!this.disable.alignments){
45487             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45488             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45489             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45490         }
45491         if(!Roo.isSafari && !this.disable.lists){
45492             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45493             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45494         }
45495         
45496         var ans = this.editorcore.getAllAncestors();
45497         if (this.formatCombo) {
45498             
45499             
45500             var store = this.formatCombo.store;
45501             this.formatCombo.setValue("");
45502             for (var i =0; i < ans.length;i++) {
45503                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45504                     // select it..
45505                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45506                     break;
45507                 }
45508             }
45509         }
45510         
45511         
45512         
45513         // hides menus... - so this cant be on a menu...
45514         Roo.menu.MenuMgr.hideAll();
45515
45516         //this.editorsyncValue();
45517     },
45518    
45519     
45520     createFontOptions : function(){
45521         var buf = [], fs = this.fontFamilies, ff, lc;
45522         
45523         
45524         
45525         for(var i = 0, len = fs.length; i< len; i++){
45526             ff = fs[i];
45527             lc = ff.toLowerCase();
45528             buf.push(
45529                 '<option value="',lc,'" style="font-family:',ff,';"',
45530                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45531                     ff,
45532                 '</option>'
45533             );
45534         }
45535         return buf.join('');
45536     },
45537     
45538     toggleSourceEdit : function(sourceEditMode){
45539         
45540         Roo.log("toolbar toogle");
45541         if(sourceEditMode === undefined){
45542             sourceEditMode = !this.sourceEditMode;
45543         }
45544         this.sourceEditMode = sourceEditMode === true;
45545         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45546         // just toggle the button?
45547         if(btn.pressed !== this.sourceEditMode){
45548             btn.toggle(this.sourceEditMode);
45549             return;
45550         }
45551         
45552         if(sourceEditMode){
45553             Roo.log("disabling buttons");
45554             this.tb.items.each(function(item){
45555                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45556                     item.disable();
45557                 }
45558             });
45559           
45560         }else{
45561             Roo.log("enabling buttons");
45562             if(this.editorcore.initialized){
45563                 this.tb.items.each(function(item){
45564                     item.enable();
45565                 });
45566             }
45567             
45568         }
45569         Roo.log("calling toggole on editor");
45570         // tell the editor that it's been pressed..
45571         this.editor.toggleSourceEdit(sourceEditMode);
45572        
45573     },
45574      /**
45575      * Object collection of toolbar tooltips for the buttons in the editor. The key
45576      * is the command id associated with that button and the value is a valid QuickTips object.
45577      * For example:
45578 <pre><code>
45579 {
45580     bold : {
45581         title: 'Bold (Ctrl+B)',
45582         text: 'Make the selected text bold.',
45583         cls: 'x-html-editor-tip'
45584     },
45585     italic : {
45586         title: 'Italic (Ctrl+I)',
45587         text: 'Make the selected text italic.',
45588         cls: 'x-html-editor-tip'
45589     },
45590     ...
45591 </code></pre>
45592     * @type Object
45593      */
45594     buttonTips : {
45595         bold : {
45596             title: 'Bold (Ctrl+B)',
45597             text: 'Make the selected text bold.',
45598             cls: 'x-html-editor-tip'
45599         },
45600         italic : {
45601             title: 'Italic (Ctrl+I)',
45602             text: 'Make the selected text italic.',
45603             cls: 'x-html-editor-tip'
45604         },
45605         underline : {
45606             title: 'Underline (Ctrl+U)',
45607             text: 'Underline the selected text.',
45608             cls: 'x-html-editor-tip'
45609         },
45610         strikethrough : {
45611             title: 'Strikethrough',
45612             text: 'Strikethrough the selected text.',
45613             cls: 'x-html-editor-tip'
45614         },
45615         increasefontsize : {
45616             title: 'Grow Text',
45617             text: 'Increase the font size.',
45618             cls: 'x-html-editor-tip'
45619         },
45620         decreasefontsize : {
45621             title: 'Shrink Text',
45622             text: 'Decrease the font size.',
45623             cls: 'x-html-editor-tip'
45624         },
45625         backcolor : {
45626             title: 'Text Highlight Color',
45627             text: 'Change the background color of the selected text.',
45628             cls: 'x-html-editor-tip'
45629         },
45630         forecolor : {
45631             title: 'Font Color',
45632             text: 'Change the color of the selected text.',
45633             cls: 'x-html-editor-tip'
45634         },
45635         justifyleft : {
45636             title: 'Align Text Left',
45637             text: 'Align text to the left.',
45638             cls: 'x-html-editor-tip'
45639         },
45640         justifycenter : {
45641             title: 'Center Text',
45642             text: 'Center text in the editor.',
45643             cls: 'x-html-editor-tip'
45644         },
45645         justifyright : {
45646             title: 'Align Text Right',
45647             text: 'Align text to the right.',
45648             cls: 'x-html-editor-tip'
45649         },
45650         insertunorderedlist : {
45651             title: 'Bullet List',
45652             text: 'Start a bulleted list.',
45653             cls: 'x-html-editor-tip'
45654         },
45655         insertorderedlist : {
45656             title: 'Numbered List',
45657             text: 'Start a numbered list.',
45658             cls: 'x-html-editor-tip'
45659         },
45660         createlink : {
45661             title: 'Hyperlink',
45662             text: 'Make the selected text a hyperlink.',
45663             cls: 'x-html-editor-tip'
45664         },
45665         sourceedit : {
45666             title: 'Source Edit',
45667             text: 'Switch to source editing mode.',
45668             cls: 'x-html-editor-tip'
45669         }
45670     },
45671     // private
45672     onDestroy : function(){
45673         if(this.rendered){
45674             
45675             this.tb.items.each(function(item){
45676                 if(item.menu){
45677                     item.menu.removeAll();
45678                     if(item.menu.el){
45679                         item.menu.el.destroy();
45680                     }
45681                 }
45682                 item.destroy();
45683             });
45684              
45685         }
45686     },
45687     onFirstFocus: function() {
45688         this.tb.items.each(function(item){
45689            item.enable();
45690         });
45691     }
45692 });
45693
45694
45695
45696
45697 // <script type="text/javascript">
45698 /*
45699  * Based on
45700  * Ext JS Library 1.1.1
45701  * Copyright(c) 2006-2007, Ext JS, LLC.
45702  *  
45703  
45704  */
45705
45706  
45707 /**
45708  * @class Roo.form.HtmlEditor.ToolbarContext
45709  * Context Toolbar
45710  * 
45711  * Usage:
45712  *
45713  new Roo.form.HtmlEditor({
45714     ....
45715     toolbars : [
45716         { xtype: 'ToolbarStandard', styles : {} }
45717         { xtype: 'ToolbarContext', disable : {} }
45718     ]
45719 })
45720
45721      
45722  * 
45723  * @config : {Object} disable List of elements to disable.. (not done yet.)
45724  * @config : {Object} styles  Map of styles available.
45725  * 
45726  */
45727
45728 Roo.form.HtmlEditor.ToolbarContext = function(config)
45729 {
45730     
45731     Roo.apply(this, config);
45732     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45733     // dont call parent... till later.
45734     this.styles = this.styles || {};
45735 }
45736
45737  
45738
45739 Roo.form.HtmlEditor.ToolbarContext.types = {
45740     'IMG' : {
45741         width : {
45742             title: "Width",
45743             width: 40
45744         },
45745         height:  {
45746             title: "Height",
45747             width: 40
45748         },
45749         align: {
45750             title: "Align",
45751             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45752             width : 80
45753             
45754         },
45755         border: {
45756             title: "Border",
45757             width: 40
45758         },
45759         alt: {
45760             title: "Alt",
45761             width: 120
45762         },
45763         src : {
45764             title: "Src",
45765             width: 220
45766         }
45767         
45768     },
45769     'A' : {
45770         name : {
45771             title: "Name",
45772             width: 50
45773         },
45774         target:  {
45775             title: "Target",
45776             width: 120
45777         },
45778         href:  {
45779             title: "Href",
45780             width: 220
45781         } // border?
45782         
45783     },
45784     'TABLE' : {
45785         rows : {
45786             title: "Rows",
45787             width: 20
45788         },
45789         cols : {
45790             title: "Cols",
45791             width: 20
45792         },
45793         width : {
45794             title: "Width",
45795             width: 40
45796         },
45797         height : {
45798             title: "Height",
45799             width: 40
45800         },
45801         border : {
45802             title: "Border",
45803             width: 20
45804         }
45805     },
45806     'TD' : {
45807         width : {
45808             title: "Width",
45809             width: 40
45810         },
45811         height : {
45812             title: "Height",
45813             width: 40
45814         },   
45815         align: {
45816             title: "Align",
45817             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45818             width: 80
45819         },
45820         valign: {
45821             title: "Valign",
45822             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45823             width: 80
45824         },
45825         colspan: {
45826             title: "Colspan",
45827             width: 20
45828             
45829         },
45830          'font-family'  : {
45831             title : "Font",
45832             style : 'fontFamily',
45833             displayField: 'display',
45834             optname : 'font-family',
45835             width: 140
45836         }
45837     },
45838     'INPUT' : {
45839         name : {
45840             title: "name",
45841             width: 120
45842         },
45843         value : {
45844             title: "Value",
45845             width: 120
45846         },
45847         width : {
45848             title: "Width",
45849             width: 40
45850         }
45851     },
45852     'LABEL' : {
45853         'for' : {
45854             title: "For",
45855             width: 120
45856         }
45857     },
45858     'TEXTAREA' : {
45859           name : {
45860             title: "name",
45861             width: 120
45862         },
45863         rows : {
45864             title: "Rows",
45865             width: 20
45866         },
45867         cols : {
45868             title: "Cols",
45869             width: 20
45870         }
45871     },
45872     'SELECT' : {
45873         name : {
45874             title: "name",
45875             width: 120
45876         },
45877         selectoptions : {
45878             title: "Options",
45879             width: 200
45880         }
45881     },
45882     
45883     // should we really allow this??
45884     // should this just be 
45885     'BODY' : {
45886         title : {
45887             title: "Title",
45888             width: 200,
45889             disabled : true
45890         }
45891     },
45892     'SPAN' : {
45893         'font-family'  : {
45894             title : "Font",
45895             style : 'fontFamily',
45896             displayField: 'display',
45897             optname : 'font-family',
45898             width: 140
45899         }
45900     },
45901     'DIV' : {
45902         'font-family'  : {
45903             title : "Font",
45904             style : 'fontFamily',
45905             displayField: 'display',
45906             optname : 'font-family',
45907             width: 140
45908         }
45909     },
45910      'P' : {
45911         'font-family'  : {
45912             title : "Font",
45913             style : 'fontFamily',
45914             displayField: 'display',
45915             optname : 'font-family',
45916             width: 140
45917         }
45918     },
45919     
45920     '*' : {
45921         // empty..
45922     }
45923
45924 };
45925
45926 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45927 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45928
45929 Roo.form.HtmlEditor.ToolbarContext.options = {
45930         'font-family'  : [ 
45931                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45932                 [ 'Courier New', 'Courier New'],
45933                 [ 'Tahoma', 'Tahoma'],
45934                 [ 'Times New Roman,serif', 'Times'],
45935                 [ 'Verdana','Verdana' ]
45936         ]
45937 };
45938
45939 // fixme - these need to be configurable..
45940  
45941
45942 //Roo.form.HtmlEditor.ToolbarContext.types
45943
45944
45945 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
45946     
45947     tb: false,
45948     
45949     rendered: false,
45950     
45951     editor : false,
45952     editorcore : false,
45953     /**
45954      * @cfg {Object} disable  List of toolbar elements to disable
45955          
45956      */
45957     disable : false,
45958     /**
45959      * @cfg {Object} styles List of styles 
45960      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
45961      *
45962      * These must be defined in the page, so they get rendered correctly..
45963      * .headline { }
45964      * TD.underline { }
45965      * 
45966      */
45967     styles : false,
45968     
45969     options: false,
45970     
45971     toolbars : false,
45972     
45973     init : function(editor)
45974     {
45975         this.editor = editor;
45976         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45977         var editorcore = this.editorcore;
45978         
45979         var fid = editorcore.frameId;
45980         var etb = this;
45981         function btn(id, toggle, handler){
45982             var xid = fid + '-'+ id ;
45983             return {
45984                 id : xid,
45985                 cmd : id,
45986                 cls : 'x-btn-icon x-edit-'+id,
45987                 enableToggle:toggle !== false,
45988                 scope: editorcore, // was editor...
45989                 handler:handler||editorcore.relayBtnCmd,
45990                 clickEvent:'mousedown',
45991                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45992                 tabIndex:-1
45993             };
45994         }
45995         // create a new element.
45996         var wdiv = editor.wrap.createChild({
45997                 tag: 'div'
45998             }, editor.wrap.dom.firstChild.nextSibling, true);
45999         
46000         // can we do this more than once??
46001         
46002          // stop form submits
46003       
46004  
46005         // disable everything...
46006         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46007         this.toolbars = {};
46008            
46009         for (var i in  ty) {
46010           
46011             this.toolbars[i] = this.buildToolbar(ty[i],i);
46012         }
46013         this.tb = this.toolbars.BODY;
46014         this.tb.el.show();
46015         this.buildFooter();
46016         this.footer.show();
46017         editor.on('hide', function( ) { this.footer.hide() }, this);
46018         editor.on('show', function( ) { this.footer.show() }, this);
46019         
46020          
46021         this.rendered = true;
46022         
46023         // the all the btns;
46024         editor.on('editorevent', this.updateToolbar, this);
46025         // other toolbars need to implement this..
46026         //editor.on('editmodechange', this.updateToolbar, this);
46027     },
46028     
46029     
46030     
46031     /**
46032      * Protected method that will not generally be called directly. It triggers
46033      * a toolbar update by reading the markup state of the current selection in the editor.
46034      *
46035      * Note you can force an update by calling on('editorevent', scope, false)
46036      */
46037     updateToolbar: function(editor,ev,sel){
46038
46039         //Roo.log(ev);
46040         // capture mouse up - this is handy for selecting images..
46041         // perhaps should go somewhere else...
46042         if(!this.editorcore.activated){
46043              this.editor.onFirstFocus();
46044             return;
46045         }
46046         
46047         
46048         
46049         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46050         // selectNode - might want to handle IE?
46051         if (ev &&
46052             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46053             ev.target && ev.target.tagName == 'IMG') {
46054             // they have click on an image...
46055             // let's see if we can change the selection...
46056             sel = ev.target;
46057          
46058               var nodeRange = sel.ownerDocument.createRange();
46059             try {
46060                 nodeRange.selectNode(sel);
46061             } catch (e) {
46062                 nodeRange.selectNodeContents(sel);
46063             }
46064             //nodeRange.collapse(true);
46065             var s = this.editorcore.win.getSelection();
46066             s.removeAllRanges();
46067             s.addRange(nodeRange);
46068         }  
46069         
46070       
46071         var updateFooter = sel ? false : true;
46072         
46073         
46074         var ans = this.editorcore.getAllAncestors();
46075         
46076         // pick
46077         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46078         
46079         if (!sel) { 
46080             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46081             sel = sel ? sel : this.editorcore.doc.body;
46082             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46083             
46084         }
46085         // pick a menu that exists..
46086         var tn = sel.tagName.toUpperCase();
46087         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46088         
46089         tn = sel.tagName.toUpperCase();
46090         
46091         var lastSel = this.tb.selectedNode;
46092         
46093         this.tb.selectedNode = sel;
46094         
46095         // if current menu does not match..
46096         
46097         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46098                 
46099             this.tb.el.hide();
46100             ///console.log("show: " + tn);
46101             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46102             this.tb.el.show();
46103             // update name
46104             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46105             
46106             
46107             // update attributes
46108             if (this.tb.fields) {
46109                 this.tb.fields.each(function(e) {
46110                     if (e.stylename) {
46111                         e.setValue(sel.style[e.stylename]);
46112                         return;
46113                     } 
46114                    e.setValue(sel.getAttribute(e.attrname));
46115                 });
46116             }
46117             
46118             var hasStyles = false;
46119             for(var i in this.styles) {
46120                 hasStyles = true;
46121                 break;
46122             }
46123             
46124             // update styles
46125             if (hasStyles) { 
46126                 var st = this.tb.fields.item(0);
46127                 
46128                 st.store.removeAll();
46129                
46130                 
46131                 var cn = sel.className.split(/\s+/);
46132                 
46133                 var avs = [];
46134                 if (this.styles['*']) {
46135                     
46136                     Roo.each(this.styles['*'], function(v) {
46137                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46138                     });
46139                 }
46140                 if (this.styles[tn]) { 
46141                     Roo.each(this.styles[tn], function(v) {
46142                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46143                     });
46144                 }
46145                 
46146                 st.store.loadData(avs);
46147                 st.collapse();
46148                 st.setValue(cn);
46149             }
46150             // flag our selected Node.
46151             this.tb.selectedNode = sel;
46152            
46153            
46154             Roo.menu.MenuMgr.hideAll();
46155
46156         }
46157         
46158         if (!updateFooter) {
46159             //this.footDisp.dom.innerHTML = ''; 
46160             return;
46161         }
46162         // update the footer
46163         //
46164         var html = '';
46165         
46166         this.footerEls = ans.reverse();
46167         Roo.each(this.footerEls, function(a,i) {
46168             if (!a) { return; }
46169             html += html.length ? ' &gt; '  :  '';
46170             
46171             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46172             
46173         });
46174        
46175         // 
46176         var sz = this.footDisp.up('td').getSize();
46177         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46178         this.footDisp.dom.style.marginLeft = '5px';
46179         
46180         this.footDisp.dom.style.overflow = 'hidden';
46181         
46182         this.footDisp.dom.innerHTML = html;
46183             
46184         //this.editorsyncValue();
46185     },
46186      
46187     
46188    
46189        
46190     // private
46191     onDestroy : function(){
46192         if(this.rendered){
46193             
46194             this.tb.items.each(function(item){
46195                 if(item.menu){
46196                     item.menu.removeAll();
46197                     if(item.menu.el){
46198                         item.menu.el.destroy();
46199                     }
46200                 }
46201                 item.destroy();
46202             });
46203              
46204         }
46205     },
46206     onFirstFocus: function() {
46207         // need to do this for all the toolbars..
46208         this.tb.items.each(function(item){
46209            item.enable();
46210         });
46211     },
46212     buildToolbar: function(tlist, nm)
46213     {
46214         var editor = this.editor;
46215         var editorcore = this.editorcore;
46216          // create a new element.
46217         var wdiv = editor.wrap.createChild({
46218                 tag: 'div'
46219             }, editor.wrap.dom.firstChild.nextSibling, true);
46220         
46221        
46222         var tb = new Roo.Toolbar(wdiv);
46223         // add the name..
46224         
46225         tb.add(nm+ ":&nbsp;");
46226         
46227         var styles = [];
46228         for(var i in this.styles) {
46229             styles.push(i);
46230         }
46231         
46232         // styles...
46233         if (styles && styles.length) {
46234             
46235             // this needs a multi-select checkbox...
46236             tb.addField( new Roo.form.ComboBox({
46237                 store: new Roo.data.SimpleStore({
46238                     id : 'val',
46239                     fields: ['val', 'selected'],
46240                     data : [] 
46241                 }),
46242                 name : '-roo-edit-className',
46243                 attrname : 'className',
46244                 displayField: 'val',
46245                 typeAhead: false,
46246                 mode: 'local',
46247                 editable : false,
46248                 triggerAction: 'all',
46249                 emptyText:'Select Style',
46250                 selectOnFocus:true,
46251                 width: 130,
46252                 listeners : {
46253                     'select': function(c, r, i) {
46254                         // initial support only for on class per el..
46255                         tb.selectedNode.className =  r ? r.get('val') : '';
46256                         editorcore.syncValue();
46257                     }
46258                 }
46259     
46260             }));
46261         }
46262         
46263         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46264         var tbops = tbc.options;
46265         
46266         for (var i in tlist) {
46267             
46268             var item = tlist[i];
46269             tb.add(item.title + ":&nbsp;");
46270             
46271             
46272             //optname == used so you can configure the options available..
46273             var opts = item.opts ? item.opts : false;
46274             if (item.optname) {
46275                 opts = tbops[item.optname];
46276            
46277             }
46278             
46279             if (opts) {
46280                 // opts == pulldown..
46281                 tb.addField( new Roo.form.ComboBox({
46282                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46283                         id : 'val',
46284                         fields: ['val', 'display'],
46285                         data : opts  
46286                     }),
46287                     name : '-roo-edit-' + i,
46288                     attrname : i,
46289                     stylename : item.style ? item.style : false,
46290                     displayField: item.displayField ? item.displayField : 'val',
46291                     valueField :  'val',
46292                     typeAhead: false,
46293                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46294                     editable : false,
46295                     triggerAction: 'all',
46296                     emptyText:'Select',
46297                     selectOnFocus:true,
46298                     width: item.width ? item.width  : 130,
46299                     listeners : {
46300                         'select': function(c, r, i) {
46301                             if (c.stylename) {
46302                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46303                                 return;
46304                             }
46305                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46306                         }
46307                     }
46308
46309                 }));
46310                 continue;
46311                     
46312                  
46313                 
46314                 tb.addField( new Roo.form.TextField({
46315                     name: i,
46316                     width: 100,
46317                     //allowBlank:false,
46318                     value: ''
46319                 }));
46320                 continue;
46321             }
46322             tb.addField( new Roo.form.TextField({
46323                 name: '-roo-edit-' + i,
46324                 attrname : i,
46325                 
46326                 width: item.width,
46327                 //allowBlank:true,
46328                 value: '',
46329                 listeners: {
46330                     'change' : function(f, nv, ov) {
46331                         tb.selectedNode.setAttribute(f.attrname, nv);
46332                         editorcore.syncValue();
46333                     }
46334                 }
46335             }));
46336              
46337         }
46338         
46339         var _this = this;
46340         
46341         if(nm == 'BODY'){
46342             tb.addSeparator();
46343         
46344             tb.addButton( {
46345                 text: 'Stylesheets',
46346
46347                 listeners : {
46348                     click : function ()
46349                     {
46350                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46351                     }
46352                 }
46353             });
46354         }
46355         
46356         tb.addFill();
46357         tb.addButton( {
46358             text: 'Remove Tag',
46359     
46360             listeners : {
46361                 click : function ()
46362                 {
46363                     // remove
46364                     // undo does not work.
46365                      
46366                     var sn = tb.selectedNode;
46367                     
46368                     var pn = sn.parentNode;
46369                     
46370                     var stn =  sn.childNodes[0];
46371                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46372                     while (sn.childNodes.length) {
46373                         var node = sn.childNodes[0];
46374                         sn.removeChild(node);
46375                         //Roo.log(node);
46376                         pn.insertBefore(node, sn);
46377                         
46378                     }
46379                     pn.removeChild(sn);
46380                     var range = editorcore.createRange();
46381         
46382                     range.setStart(stn,0);
46383                     range.setEnd(en,0); //????
46384                     //range.selectNode(sel);
46385                     
46386                     
46387                     var selection = editorcore.getSelection();
46388                     selection.removeAllRanges();
46389                     selection.addRange(range);
46390                     
46391                     
46392                     
46393                     //_this.updateToolbar(null, null, pn);
46394                     _this.updateToolbar(null, null, null);
46395                     _this.footDisp.dom.innerHTML = ''; 
46396                 }
46397             }
46398             
46399                     
46400                 
46401             
46402         });
46403         
46404         
46405         tb.el.on('click', function(e){
46406             e.preventDefault(); // what does this do?
46407         });
46408         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46409         tb.el.hide();
46410         tb.name = nm;
46411         // dont need to disable them... as they will get hidden
46412         return tb;
46413          
46414         
46415     },
46416     buildFooter : function()
46417     {
46418         
46419         var fel = this.editor.wrap.createChild();
46420         this.footer = new Roo.Toolbar(fel);
46421         // toolbar has scrolly on left / right?
46422         var footDisp= new Roo.Toolbar.Fill();
46423         var _t = this;
46424         this.footer.add(
46425             {
46426                 text : '&lt;',
46427                 xtype: 'Button',
46428                 handler : function() {
46429                     _t.footDisp.scrollTo('left',0,true)
46430                 }
46431             }
46432         );
46433         this.footer.add( footDisp );
46434         this.footer.add( 
46435             {
46436                 text : '&gt;',
46437                 xtype: 'Button',
46438                 handler : function() {
46439                     // no animation..
46440                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46441                 }
46442             }
46443         );
46444         var fel = Roo.get(footDisp.el);
46445         fel.addClass('x-editor-context');
46446         this.footDispWrap = fel; 
46447         this.footDispWrap.overflow  = 'hidden';
46448         
46449         this.footDisp = fel.createChild();
46450         this.footDispWrap.on('click', this.onContextClick, this)
46451         
46452         
46453     },
46454     onContextClick : function (ev,dom)
46455     {
46456         ev.preventDefault();
46457         var  cn = dom.className;
46458         //Roo.log(cn);
46459         if (!cn.match(/x-ed-loc-/)) {
46460             return;
46461         }
46462         var n = cn.split('-').pop();
46463         var ans = this.footerEls;
46464         var sel = ans[n];
46465         
46466          // pick
46467         var range = this.editorcore.createRange();
46468         
46469         range.selectNodeContents(sel);
46470         //range.selectNode(sel);
46471         
46472         
46473         var selection = this.editorcore.getSelection();
46474         selection.removeAllRanges();
46475         selection.addRange(range);
46476         
46477         
46478         
46479         this.updateToolbar(null, null, sel);
46480         
46481         
46482     }
46483     
46484     
46485     
46486     
46487     
46488 });
46489
46490
46491
46492
46493
46494 /*
46495  * Based on:
46496  * Ext JS Library 1.1.1
46497  * Copyright(c) 2006-2007, Ext JS, LLC.
46498  *
46499  * Originally Released Under LGPL - original licence link has changed is not relivant.
46500  *
46501  * Fork - LGPL
46502  * <script type="text/javascript">
46503  */
46504  
46505 /**
46506  * @class Roo.form.BasicForm
46507  * @extends Roo.util.Observable
46508  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46509  * @constructor
46510  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46511  * @param {Object} config Configuration options
46512  */
46513 Roo.form.BasicForm = function(el, config){
46514     this.allItems = [];
46515     this.childForms = [];
46516     Roo.apply(this, config);
46517     /*
46518      * The Roo.form.Field items in this form.
46519      * @type MixedCollection
46520      */
46521      
46522      
46523     this.items = new Roo.util.MixedCollection(false, function(o){
46524         return o.id || (o.id = Roo.id());
46525     });
46526     this.addEvents({
46527         /**
46528          * @event beforeaction
46529          * Fires before any action is performed. Return false to cancel the action.
46530          * @param {Form} this
46531          * @param {Action} action The action to be performed
46532          */
46533         beforeaction: true,
46534         /**
46535          * @event actionfailed
46536          * Fires when an action fails.
46537          * @param {Form} this
46538          * @param {Action} action The action that failed
46539          */
46540         actionfailed : true,
46541         /**
46542          * @event actioncomplete
46543          * Fires when an action is completed.
46544          * @param {Form} this
46545          * @param {Action} action The action that completed
46546          */
46547         actioncomplete : true
46548     });
46549     if(el){
46550         this.initEl(el);
46551     }
46552     Roo.form.BasicForm.superclass.constructor.call(this);
46553 };
46554
46555 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46556     /**
46557      * @cfg {String} method
46558      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46559      */
46560     /**
46561      * @cfg {DataReader} reader
46562      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46563      * This is optional as there is built-in support for processing JSON.
46564      */
46565     /**
46566      * @cfg {DataReader} errorReader
46567      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46568      * This is completely optional as there is built-in support for processing JSON.
46569      */
46570     /**
46571      * @cfg {String} url
46572      * The URL to use for form actions if one isn't supplied in the action options.
46573      */
46574     /**
46575      * @cfg {Boolean} fileUpload
46576      * Set to true if this form is a file upload.
46577      */
46578      
46579     /**
46580      * @cfg {Object} baseParams
46581      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46582      */
46583      /**
46584      
46585     /**
46586      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46587      */
46588     timeout: 30,
46589
46590     // private
46591     activeAction : null,
46592
46593     /**
46594      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46595      * or setValues() data instead of when the form was first created.
46596      */
46597     trackResetOnLoad : false,
46598     
46599     
46600     /**
46601      * childForms - used for multi-tab forms
46602      * @type {Array}
46603      */
46604     childForms : false,
46605     
46606     /**
46607      * allItems - full list of fields.
46608      * @type {Array}
46609      */
46610     allItems : false,
46611     
46612     /**
46613      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46614      * element by passing it or its id or mask the form itself by passing in true.
46615      * @type Mixed
46616      */
46617     waitMsgTarget : false,
46618
46619     // private
46620     initEl : function(el){
46621         this.el = Roo.get(el);
46622         this.id = this.el.id || Roo.id();
46623         this.el.on('submit', this.onSubmit, this);
46624         this.el.addClass('x-form');
46625     },
46626
46627     // private
46628     onSubmit : function(e){
46629         e.stopEvent();
46630     },
46631
46632     /**
46633      * Returns true if client-side validation on the form is successful.
46634      * @return Boolean
46635      */
46636     isValid : function(){
46637         var valid = true;
46638         this.items.each(function(f){
46639            if(!f.validate()){
46640                valid = false;
46641            }
46642         });
46643         return valid;
46644     },
46645
46646     /**
46647      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46648      * @return Boolean
46649      */
46650     isDirty : function(){
46651         var dirty = false;
46652         this.items.each(function(f){
46653            if(f.isDirty()){
46654                dirty = true;
46655                return false;
46656            }
46657         });
46658         return dirty;
46659     },
46660     
46661     /**
46662      * Returns true if any fields in this form have changed since their original load. (New version)
46663      * @return Boolean
46664      */
46665     
46666     hasChanged : function()
46667     {
46668         var dirty = false;
46669         this.items.each(function(f){
46670            if(f.hasChanged()){
46671                dirty = true;
46672                return false;
46673            }
46674         });
46675         return dirty;
46676         
46677     },
46678     /**
46679      * Resets all hasChanged to 'false' -
46680      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46681      * So hasChanged storage is only to be used for this purpose
46682      * @return Boolean
46683      */
46684     resetHasChanged : function()
46685     {
46686         this.items.each(function(f){
46687            f.resetHasChanged();
46688         });
46689         
46690     },
46691     
46692     
46693     /**
46694      * Performs a predefined action (submit or load) or custom actions you define on this form.
46695      * @param {String} actionName The name of the action type
46696      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46697      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46698      * accept other config options):
46699      * <pre>
46700 Property          Type             Description
46701 ----------------  ---------------  ----------------------------------------------------------------------------------
46702 url               String           The url for the action (defaults to the form's url)
46703 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46704 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46705 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46706                                    validate the form on the client (defaults to false)
46707      * </pre>
46708      * @return {BasicForm} this
46709      */
46710     doAction : function(action, options){
46711         if(typeof action == 'string'){
46712             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46713         }
46714         if(this.fireEvent('beforeaction', this, action) !== false){
46715             this.beforeAction(action);
46716             action.run.defer(100, action);
46717         }
46718         return this;
46719     },
46720
46721     /**
46722      * Shortcut to do a submit action.
46723      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46724      * @return {BasicForm} this
46725      */
46726     submit : function(options){
46727         this.doAction('submit', options);
46728         return this;
46729     },
46730
46731     /**
46732      * Shortcut to do a load action.
46733      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46734      * @return {BasicForm} this
46735      */
46736     load : function(options){
46737         this.doAction('load', options);
46738         return this;
46739     },
46740
46741     /**
46742      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46743      * @param {Record} record The record to edit
46744      * @return {BasicForm} this
46745      */
46746     updateRecord : function(record){
46747         record.beginEdit();
46748         var fs = record.fields;
46749         fs.each(function(f){
46750             var field = this.findField(f.name);
46751             if(field){
46752                 record.set(f.name, field.getValue());
46753             }
46754         }, this);
46755         record.endEdit();
46756         return this;
46757     },
46758
46759     /**
46760      * Loads an Roo.data.Record into this form.
46761      * @param {Record} record The record to load
46762      * @return {BasicForm} this
46763      */
46764     loadRecord : function(record){
46765         this.setValues(record.data);
46766         return this;
46767     },
46768
46769     // private
46770     beforeAction : function(action){
46771         var o = action.options;
46772         
46773        
46774         if(this.waitMsgTarget === true){
46775             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46776         }else if(this.waitMsgTarget){
46777             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46778             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46779         }else {
46780             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46781         }
46782          
46783     },
46784
46785     // private
46786     afterAction : function(action, success){
46787         this.activeAction = null;
46788         var o = action.options;
46789         
46790         if(this.waitMsgTarget === true){
46791             this.el.unmask();
46792         }else if(this.waitMsgTarget){
46793             this.waitMsgTarget.unmask();
46794         }else{
46795             Roo.MessageBox.updateProgress(1);
46796             Roo.MessageBox.hide();
46797         }
46798          
46799         if(success){
46800             if(o.reset){
46801                 this.reset();
46802             }
46803             Roo.callback(o.success, o.scope, [this, action]);
46804             this.fireEvent('actioncomplete', this, action);
46805             
46806         }else{
46807             
46808             // failure condition..
46809             // we have a scenario where updates need confirming.
46810             // eg. if a locking scenario exists..
46811             // we look for { errors : { needs_confirm : true }} in the response.
46812             if (
46813                 (typeof(action.result) != 'undefined')  &&
46814                 (typeof(action.result.errors) != 'undefined')  &&
46815                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46816            ){
46817                 var _t = this;
46818                 Roo.MessageBox.confirm(
46819                     "Change requires confirmation",
46820                     action.result.errorMsg,
46821                     function(r) {
46822                         if (r != 'yes') {
46823                             return;
46824                         }
46825                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46826                     }
46827                     
46828                 );
46829                 
46830                 
46831                 
46832                 return;
46833             }
46834             
46835             Roo.callback(o.failure, o.scope, [this, action]);
46836             // show an error message if no failed handler is set..
46837             if (!this.hasListener('actionfailed')) {
46838                 Roo.MessageBox.alert("Error",
46839                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46840                         action.result.errorMsg :
46841                         "Saving Failed, please check your entries or try again"
46842                 );
46843             }
46844             
46845             this.fireEvent('actionfailed', this, action);
46846         }
46847         
46848     },
46849
46850     /**
46851      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46852      * @param {String} id The value to search for
46853      * @return Field
46854      */
46855     findField : function(id){
46856         var field = this.items.get(id);
46857         if(!field){
46858             this.items.each(function(f){
46859                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46860                     field = f;
46861                     return false;
46862                 }
46863             });
46864         }
46865         return field || null;
46866     },
46867
46868     /**
46869      * Add a secondary form to this one, 
46870      * Used to provide tabbed forms. One form is primary, with hidden values 
46871      * which mirror the elements from the other forms.
46872      * 
46873      * @param {Roo.form.Form} form to add.
46874      * 
46875      */
46876     addForm : function(form)
46877     {
46878        
46879         if (this.childForms.indexOf(form) > -1) {
46880             // already added..
46881             return;
46882         }
46883         this.childForms.push(form);
46884         var n = '';
46885         Roo.each(form.allItems, function (fe) {
46886             
46887             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46888             if (this.findField(n)) { // already added..
46889                 return;
46890             }
46891             var add = new Roo.form.Hidden({
46892                 name : n
46893             });
46894             add.render(this.el);
46895             
46896             this.add( add );
46897         }, this);
46898         
46899     },
46900     /**
46901      * Mark fields in this form invalid in bulk.
46902      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46903      * @return {BasicForm} this
46904      */
46905     markInvalid : function(errors){
46906         if(errors instanceof Array){
46907             for(var i = 0, len = errors.length; i < len; i++){
46908                 var fieldError = errors[i];
46909                 var f = this.findField(fieldError.id);
46910                 if(f){
46911                     f.markInvalid(fieldError.msg);
46912                 }
46913             }
46914         }else{
46915             var field, id;
46916             for(id in errors){
46917                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46918                     field.markInvalid(errors[id]);
46919                 }
46920             }
46921         }
46922         Roo.each(this.childForms || [], function (f) {
46923             f.markInvalid(errors);
46924         });
46925         
46926         return this;
46927     },
46928
46929     /**
46930      * Set values for fields in this form in bulk.
46931      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46932      * @return {BasicForm} this
46933      */
46934     setValues : function(values){
46935         if(values instanceof Array){ // array of objects
46936             for(var i = 0, len = values.length; i < len; i++){
46937                 var v = values[i];
46938                 var f = this.findField(v.id);
46939                 if(f){
46940                     f.setValue(v.value);
46941                     if(this.trackResetOnLoad){
46942                         f.originalValue = f.getValue();
46943                     }
46944                 }
46945             }
46946         }else{ // object hash
46947             var field, id;
46948             for(id in values){
46949                 if(typeof values[id] != 'function' && (field = this.findField(id))){
46950                     
46951                     if (field.setFromData && 
46952                         field.valueField && 
46953                         field.displayField &&
46954                         // combos' with local stores can 
46955                         // be queried via setValue()
46956                         // to set their value..
46957                         (field.store && !field.store.isLocal)
46958                         ) {
46959                         // it's a combo
46960                         var sd = { };
46961                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
46962                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
46963                         field.setFromData(sd);
46964                         
46965                     } else {
46966                         field.setValue(values[id]);
46967                     }
46968                     
46969                     
46970                     if(this.trackResetOnLoad){
46971                         field.originalValue = field.getValue();
46972                     }
46973                 }
46974             }
46975         }
46976         this.resetHasChanged();
46977         
46978         
46979         Roo.each(this.childForms || [], function (f) {
46980             f.setValues(values);
46981             f.resetHasChanged();
46982         });
46983                 
46984         return this;
46985     },
46986
46987     /**
46988      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
46989      * they are returned as an array.
46990      * @param {Boolean} asString
46991      * @return {Object}
46992      */
46993     getValues : function(asString){
46994         if (this.childForms) {
46995             // copy values from the child forms
46996             Roo.each(this.childForms, function (f) {
46997                 this.setValues(f.getValues());
46998             }, this);
46999         }
47000         
47001         
47002         
47003         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47004         if(asString === true){
47005             return fs;
47006         }
47007         return Roo.urlDecode(fs);
47008     },
47009     
47010     /**
47011      * Returns the fields in this form as an object with key/value pairs. 
47012      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47013      * @return {Object}
47014      */
47015     getFieldValues : function(with_hidden)
47016     {
47017         if (this.childForms) {
47018             // copy values from the child forms
47019             // should this call getFieldValues - probably not as we do not currently copy
47020             // hidden fields when we generate..
47021             Roo.each(this.childForms, function (f) {
47022                 this.setValues(f.getValues());
47023             }, this);
47024         }
47025         
47026         var ret = {};
47027         this.items.each(function(f){
47028             if (!f.getName()) {
47029                 return;
47030             }
47031             var v = f.getValue();
47032             if (f.inputType =='radio') {
47033                 if (typeof(ret[f.getName()]) == 'undefined') {
47034                     ret[f.getName()] = ''; // empty..
47035                 }
47036                 
47037                 if (!f.el.dom.checked) {
47038                     return;
47039                     
47040                 }
47041                 v = f.el.dom.value;
47042                 
47043             }
47044             
47045             // not sure if this supported any more..
47046             if ((typeof(v) == 'object') && f.getRawValue) {
47047                 v = f.getRawValue() ; // dates..
47048             }
47049             // combo boxes where name != hiddenName...
47050             if (f.name != f.getName()) {
47051                 ret[f.name] = f.getRawValue();
47052             }
47053             ret[f.getName()] = v;
47054         });
47055         
47056         return ret;
47057     },
47058
47059     /**
47060      * Clears all invalid messages in this form.
47061      * @return {BasicForm} this
47062      */
47063     clearInvalid : function(){
47064         this.items.each(function(f){
47065            f.clearInvalid();
47066         });
47067         
47068         Roo.each(this.childForms || [], function (f) {
47069             f.clearInvalid();
47070         });
47071         
47072         
47073         return this;
47074     },
47075
47076     /**
47077      * Resets this form.
47078      * @return {BasicForm} this
47079      */
47080     reset : function(){
47081         this.items.each(function(f){
47082             f.reset();
47083         });
47084         
47085         Roo.each(this.childForms || [], function (f) {
47086             f.reset();
47087         });
47088         this.resetHasChanged();
47089         
47090         return this;
47091     },
47092
47093     /**
47094      * Add Roo.form components to this form.
47095      * @param {Field} field1
47096      * @param {Field} field2 (optional)
47097      * @param {Field} etc (optional)
47098      * @return {BasicForm} this
47099      */
47100     add : function(){
47101         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47102         return this;
47103     },
47104
47105
47106     /**
47107      * Removes a field from the items collection (does NOT remove its markup).
47108      * @param {Field} field
47109      * @return {BasicForm} this
47110      */
47111     remove : function(field){
47112         this.items.remove(field);
47113         return this;
47114     },
47115
47116     /**
47117      * Looks at the fields in this form, checks them for an id attribute,
47118      * and calls applyTo on the existing dom element with that id.
47119      * @return {BasicForm} this
47120      */
47121     render : function(){
47122         this.items.each(function(f){
47123             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47124                 f.applyTo(f.id);
47125             }
47126         });
47127         return this;
47128     },
47129
47130     /**
47131      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47132      * @param {Object} values
47133      * @return {BasicForm} this
47134      */
47135     applyToFields : function(o){
47136         this.items.each(function(f){
47137            Roo.apply(f, o);
47138         });
47139         return this;
47140     },
47141
47142     /**
47143      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47144      * @param {Object} values
47145      * @return {BasicForm} this
47146      */
47147     applyIfToFields : function(o){
47148         this.items.each(function(f){
47149            Roo.applyIf(f, o);
47150         });
47151         return this;
47152     }
47153 });
47154
47155 // back compat
47156 Roo.BasicForm = Roo.form.BasicForm;/*
47157  * Based on:
47158  * Ext JS Library 1.1.1
47159  * Copyright(c) 2006-2007, Ext JS, LLC.
47160  *
47161  * Originally Released Under LGPL - original licence link has changed is not relivant.
47162  *
47163  * Fork - LGPL
47164  * <script type="text/javascript">
47165  */
47166
47167 /**
47168  * @class Roo.form.Form
47169  * @extends Roo.form.BasicForm
47170  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47171  * @constructor
47172  * @param {Object} config Configuration options
47173  */
47174 Roo.form.Form = function(config){
47175     var xitems =  [];
47176     if (config.items) {
47177         xitems = config.items;
47178         delete config.items;
47179     }
47180    
47181     
47182     Roo.form.Form.superclass.constructor.call(this, null, config);
47183     this.url = this.url || this.action;
47184     if(!this.root){
47185         this.root = new Roo.form.Layout(Roo.applyIf({
47186             id: Roo.id()
47187         }, config));
47188     }
47189     this.active = this.root;
47190     /**
47191      * Array of all the buttons that have been added to this form via {@link addButton}
47192      * @type Array
47193      */
47194     this.buttons = [];
47195     this.allItems = [];
47196     this.addEvents({
47197         /**
47198          * @event clientvalidation
47199          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47200          * @param {Form} this
47201          * @param {Boolean} valid true if the form has passed client-side validation
47202          */
47203         clientvalidation: true,
47204         /**
47205          * @event rendered
47206          * Fires when the form is rendered
47207          * @param {Roo.form.Form} form
47208          */
47209         rendered : true
47210     });
47211     
47212     if (this.progressUrl) {
47213             // push a hidden field onto the list of fields..
47214             this.addxtype( {
47215                     xns: Roo.form, 
47216                     xtype : 'Hidden', 
47217                     name : 'UPLOAD_IDENTIFIER' 
47218             });
47219         }
47220         
47221     
47222     Roo.each(xitems, this.addxtype, this);
47223     
47224     
47225     
47226 };
47227
47228 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47229     /**
47230      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47231      */
47232     /**
47233      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47234      */
47235     /**
47236      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47237      */
47238     buttonAlign:'center',
47239
47240     /**
47241      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47242      */
47243     minButtonWidth:75,
47244
47245     /**
47246      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47247      * This property cascades to child containers if not set.
47248      */
47249     labelAlign:'left',
47250
47251     /**
47252      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47253      * fires a looping event with that state. This is required to bind buttons to the valid
47254      * state using the config value formBind:true on the button.
47255      */
47256     monitorValid : false,
47257
47258     /**
47259      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47260      */
47261     monitorPoll : 200,
47262     
47263     /**
47264      * @cfg {String} progressUrl - Url to return progress data 
47265      */
47266     
47267     progressUrl : false,
47268   
47269     /**
47270      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47271      * fields are added and the column is closed. If no fields are passed the column remains open
47272      * until end() is called.
47273      * @param {Object} config The config to pass to the column
47274      * @param {Field} field1 (optional)
47275      * @param {Field} field2 (optional)
47276      * @param {Field} etc (optional)
47277      * @return Column The column container object
47278      */
47279     column : function(c){
47280         var col = new Roo.form.Column(c);
47281         this.start(col);
47282         if(arguments.length > 1){ // duplicate code required because of Opera
47283             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47284             this.end();
47285         }
47286         return col;
47287     },
47288
47289     /**
47290      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47291      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47292      * until end() is called.
47293      * @param {Object} config The config to pass to the fieldset
47294      * @param {Field} field1 (optional)
47295      * @param {Field} field2 (optional)
47296      * @param {Field} etc (optional)
47297      * @return FieldSet The fieldset container object
47298      */
47299     fieldset : function(c){
47300         var fs = new Roo.form.FieldSet(c);
47301         this.start(fs);
47302         if(arguments.length > 1){ // duplicate code required because of Opera
47303             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47304             this.end();
47305         }
47306         return fs;
47307     },
47308
47309     /**
47310      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47311      * fields are added and the container is closed. If no fields are passed the container remains open
47312      * until end() is called.
47313      * @param {Object} config The config to pass to the Layout
47314      * @param {Field} field1 (optional)
47315      * @param {Field} field2 (optional)
47316      * @param {Field} etc (optional)
47317      * @return Layout The container object
47318      */
47319     container : function(c){
47320         var l = new Roo.form.Layout(c);
47321         this.start(l);
47322         if(arguments.length > 1){ // duplicate code required because of Opera
47323             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47324             this.end();
47325         }
47326         return l;
47327     },
47328
47329     /**
47330      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47331      * @param {Object} container A Roo.form.Layout or subclass of Layout
47332      * @return {Form} this
47333      */
47334     start : function(c){
47335         // cascade label info
47336         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47337         this.active.stack.push(c);
47338         c.ownerCt = this.active;
47339         this.active = c;
47340         return this;
47341     },
47342
47343     /**
47344      * Closes the current open container
47345      * @return {Form} this
47346      */
47347     end : function(){
47348         if(this.active == this.root){
47349             return this;
47350         }
47351         this.active = this.active.ownerCt;
47352         return this;
47353     },
47354
47355     /**
47356      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47357      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47358      * as the label of the field.
47359      * @param {Field} field1
47360      * @param {Field} field2 (optional)
47361      * @param {Field} etc. (optional)
47362      * @return {Form} this
47363      */
47364     add : function(){
47365         this.active.stack.push.apply(this.active.stack, arguments);
47366         this.allItems.push.apply(this.allItems,arguments);
47367         var r = [];
47368         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47369             if(a[i].isFormField){
47370                 r.push(a[i]);
47371             }
47372         }
47373         if(r.length > 0){
47374             Roo.form.Form.superclass.add.apply(this, r);
47375         }
47376         return this;
47377     },
47378     
47379
47380     
47381     
47382     
47383      /**
47384      * Find any element that has been added to a form, using it's ID or name
47385      * This can include framesets, columns etc. along with regular fields..
47386      * @param {String} id - id or name to find.
47387      
47388      * @return {Element} e - or false if nothing found.
47389      */
47390     findbyId : function(id)
47391     {
47392         var ret = false;
47393         if (!id) {
47394             return ret;
47395         }
47396         Roo.each(this.allItems, function(f){
47397             if (f.id == id || f.name == id ){
47398                 ret = f;
47399                 return false;
47400             }
47401         });
47402         return ret;
47403     },
47404
47405     
47406     
47407     /**
47408      * Render this form into the passed container. This should only be called once!
47409      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47410      * @return {Form} this
47411      */
47412     render : function(ct)
47413     {
47414         
47415         
47416         
47417         ct = Roo.get(ct);
47418         var o = this.autoCreate || {
47419             tag: 'form',
47420             method : this.method || 'POST',
47421             id : this.id || Roo.id()
47422         };
47423         this.initEl(ct.createChild(o));
47424
47425         this.root.render(this.el);
47426         
47427        
47428              
47429         this.items.each(function(f){
47430             f.render('x-form-el-'+f.id);
47431         });
47432
47433         if(this.buttons.length > 0){
47434             // tables are required to maintain order and for correct IE layout
47435             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47436                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47437                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47438             }}, null, true);
47439             var tr = tb.getElementsByTagName('tr')[0];
47440             for(var i = 0, len = this.buttons.length; i < len; i++) {
47441                 var b = this.buttons[i];
47442                 var td = document.createElement('td');
47443                 td.className = 'x-form-btn-td';
47444                 b.render(tr.appendChild(td));
47445             }
47446         }
47447         if(this.monitorValid){ // initialize after render
47448             this.startMonitoring();
47449         }
47450         this.fireEvent('rendered', this);
47451         return this;
47452     },
47453
47454     /**
47455      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47456      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47457      * object or a valid Roo.DomHelper element config
47458      * @param {Function} handler The function called when the button is clicked
47459      * @param {Object} scope (optional) The scope of the handler function
47460      * @return {Roo.Button}
47461      */
47462     addButton : function(config, handler, scope){
47463         var bc = {
47464             handler: handler,
47465             scope: scope,
47466             minWidth: this.minButtonWidth,
47467             hideParent:true
47468         };
47469         if(typeof config == "string"){
47470             bc.text = config;
47471         }else{
47472             Roo.apply(bc, config);
47473         }
47474         var btn = new Roo.Button(null, bc);
47475         this.buttons.push(btn);
47476         return btn;
47477     },
47478
47479      /**
47480      * Adds a series of form elements (using the xtype property as the factory method.
47481      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47482      * @param {Object} config 
47483      */
47484     
47485     addxtype : function()
47486     {
47487         var ar = Array.prototype.slice.call(arguments, 0);
47488         var ret = false;
47489         for(var i = 0; i < ar.length; i++) {
47490             if (!ar[i]) {
47491                 continue; // skip -- if this happends something invalid got sent, we 
47492                 // should ignore it, as basically that interface element will not show up
47493                 // and that should be pretty obvious!!
47494             }
47495             
47496             if (Roo.form[ar[i].xtype]) {
47497                 ar[i].form = this;
47498                 var fe = Roo.factory(ar[i], Roo.form);
47499                 if (!ret) {
47500                     ret = fe;
47501                 }
47502                 fe.form = this;
47503                 if (fe.store) {
47504                     fe.store.form = this;
47505                 }
47506                 if (fe.isLayout) {  
47507                          
47508                     this.start(fe);
47509                     this.allItems.push(fe);
47510                     if (fe.items && fe.addxtype) {
47511                         fe.addxtype.apply(fe, fe.items);
47512                         delete fe.items;
47513                     }
47514                      this.end();
47515                     continue;
47516                 }
47517                 
47518                 
47519                  
47520                 this.add(fe);
47521               //  console.log('adding ' + ar[i].xtype);
47522             }
47523             if (ar[i].xtype == 'Button') {  
47524                 //console.log('adding button');
47525                 //console.log(ar[i]);
47526                 this.addButton(ar[i]);
47527                 this.allItems.push(fe);
47528                 continue;
47529             }
47530             
47531             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47532                 alert('end is not supported on xtype any more, use items');
47533             //    this.end();
47534             //    //console.log('adding end');
47535             }
47536             
47537         }
47538         return ret;
47539     },
47540     
47541     /**
47542      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47543      * option "monitorValid"
47544      */
47545     startMonitoring : function(){
47546         if(!this.bound){
47547             this.bound = true;
47548             Roo.TaskMgr.start({
47549                 run : this.bindHandler,
47550                 interval : this.monitorPoll || 200,
47551                 scope: this
47552             });
47553         }
47554     },
47555
47556     /**
47557      * Stops monitoring of the valid state of this form
47558      */
47559     stopMonitoring : function(){
47560         this.bound = false;
47561     },
47562
47563     // private
47564     bindHandler : function(){
47565         if(!this.bound){
47566             return false; // stops binding
47567         }
47568         var valid = true;
47569         this.items.each(function(f){
47570             if(!f.isValid(true)){
47571                 valid = false;
47572                 return false;
47573             }
47574         });
47575         for(var i = 0, len = this.buttons.length; i < len; i++){
47576             var btn = this.buttons[i];
47577             if(btn.formBind === true && btn.disabled === valid){
47578                 btn.setDisabled(!valid);
47579             }
47580         }
47581         this.fireEvent('clientvalidation', this, valid);
47582     }
47583     
47584     
47585     
47586     
47587     
47588     
47589     
47590     
47591 });
47592
47593
47594 // back compat
47595 Roo.Form = Roo.form.Form;
47596 /*
47597  * Based on:
47598  * Ext JS Library 1.1.1
47599  * Copyright(c) 2006-2007, Ext JS, LLC.
47600  *
47601  * Originally Released Under LGPL - original licence link has changed is not relivant.
47602  *
47603  * Fork - LGPL
47604  * <script type="text/javascript">
47605  */
47606
47607 // as we use this in bootstrap.
47608 Roo.namespace('Roo.form');
47609  /**
47610  * @class Roo.form.Action
47611  * Internal Class used to handle form actions
47612  * @constructor
47613  * @param {Roo.form.BasicForm} el The form element or its id
47614  * @param {Object} config Configuration options
47615  */
47616
47617  
47618  
47619 // define the action interface
47620 Roo.form.Action = function(form, options){
47621     this.form = form;
47622     this.options = options || {};
47623 };
47624 /**
47625  * Client Validation Failed
47626  * @const 
47627  */
47628 Roo.form.Action.CLIENT_INVALID = 'client';
47629 /**
47630  * Server Validation Failed
47631  * @const 
47632  */
47633 Roo.form.Action.SERVER_INVALID = 'server';
47634  /**
47635  * Connect to Server Failed
47636  * @const 
47637  */
47638 Roo.form.Action.CONNECT_FAILURE = 'connect';
47639 /**
47640  * Reading Data from Server Failed
47641  * @const 
47642  */
47643 Roo.form.Action.LOAD_FAILURE = 'load';
47644
47645 Roo.form.Action.prototype = {
47646     type : 'default',
47647     failureType : undefined,
47648     response : undefined,
47649     result : undefined,
47650
47651     // interface method
47652     run : function(options){
47653
47654     },
47655
47656     // interface method
47657     success : function(response){
47658
47659     },
47660
47661     // interface method
47662     handleResponse : function(response){
47663
47664     },
47665
47666     // default connection failure
47667     failure : function(response){
47668         
47669         this.response = response;
47670         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47671         this.form.afterAction(this, false);
47672     },
47673
47674     processResponse : function(response){
47675         this.response = response;
47676         if(!response.responseText){
47677             return true;
47678         }
47679         this.result = this.handleResponse(response);
47680         return this.result;
47681     },
47682
47683     // utility functions used internally
47684     getUrl : function(appendParams){
47685         var url = this.options.url || this.form.url || this.form.el.dom.action;
47686         if(appendParams){
47687             var p = this.getParams();
47688             if(p){
47689                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47690             }
47691         }
47692         return url;
47693     },
47694
47695     getMethod : function(){
47696         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47697     },
47698
47699     getParams : function(){
47700         var bp = this.form.baseParams;
47701         var p = this.options.params;
47702         if(p){
47703             if(typeof p == "object"){
47704                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47705             }else if(typeof p == 'string' && bp){
47706                 p += '&' + Roo.urlEncode(bp);
47707             }
47708         }else if(bp){
47709             p = Roo.urlEncode(bp);
47710         }
47711         return p;
47712     },
47713
47714     createCallback : function(){
47715         return {
47716             success: this.success,
47717             failure: this.failure,
47718             scope: this,
47719             timeout: (this.form.timeout*1000),
47720             upload: this.form.fileUpload ? this.success : undefined
47721         };
47722     }
47723 };
47724
47725 Roo.form.Action.Submit = function(form, options){
47726     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47727 };
47728
47729 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47730     type : 'submit',
47731
47732     haveProgress : false,
47733     uploadComplete : false,
47734     
47735     // uploadProgress indicator.
47736     uploadProgress : function()
47737     {
47738         if (!this.form.progressUrl) {
47739             return;
47740         }
47741         
47742         if (!this.haveProgress) {
47743             Roo.MessageBox.progress("Uploading", "Uploading");
47744         }
47745         if (this.uploadComplete) {
47746            Roo.MessageBox.hide();
47747            return;
47748         }
47749         
47750         this.haveProgress = true;
47751    
47752         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47753         
47754         var c = new Roo.data.Connection();
47755         c.request({
47756             url : this.form.progressUrl,
47757             params: {
47758                 id : uid
47759             },
47760             method: 'GET',
47761             success : function(req){
47762                //console.log(data);
47763                 var rdata = false;
47764                 var edata;
47765                 try  {
47766                    rdata = Roo.decode(req.responseText)
47767                 } catch (e) {
47768                     Roo.log("Invalid data from server..");
47769                     Roo.log(edata);
47770                     return;
47771                 }
47772                 if (!rdata || !rdata.success) {
47773                     Roo.log(rdata);
47774                     Roo.MessageBox.alert(Roo.encode(rdata));
47775                     return;
47776                 }
47777                 var data = rdata.data;
47778                 
47779                 if (this.uploadComplete) {
47780                    Roo.MessageBox.hide();
47781                    return;
47782                 }
47783                    
47784                 if (data){
47785                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47786                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47787                     );
47788                 }
47789                 this.uploadProgress.defer(2000,this);
47790             },
47791        
47792             failure: function(data) {
47793                 Roo.log('progress url failed ');
47794                 Roo.log(data);
47795             },
47796             scope : this
47797         });
47798            
47799     },
47800     
47801     
47802     run : function()
47803     {
47804         // run get Values on the form, so it syncs any secondary forms.
47805         this.form.getValues();
47806         
47807         var o = this.options;
47808         var method = this.getMethod();
47809         var isPost = method == 'POST';
47810         if(o.clientValidation === false || this.form.isValid()){
47811             
47812             if (this.form.progressUrl) {
47813                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47814                     (new Date() * 1) + '' + Math.random());
47815                     
47816             } 
47817             
47818             
47819             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47820                 form:this.form.el.dom,
47821                 url:this.getUrl(!isPost),
47822                 method: method,
47823                 params:isPost ? this.getParams() : null,
47824                 isUpload: this.form.fileUpload
47825             }));
47826             
47827             this.uploadProgress();
47828
47829         }else if (o.clientValidation !== false){ // client validation failed
47830             this.failureType = Roo.form.Action.CLIENT_INVALID;
47831             this.form.afterAction(this, false);
47832         }
47833     },
47834
47835     success : function(response)
47836     {
47837         this.uploadComplete= true;
47838         if (this.haveProgress) {
47839             Roo.MessageBox.hide();
47840         }
47841         
47842         
47843         var result = this.processResponse(response);
47844         if(result === true || result.success){
47845             this.form.afterAction(this, true);
47846             return;
47847         }
47848         if(result.errors){
47849             this.form.markInvalid(result.errors);
47850             this.failureType = Roo.form.Action.SERVER_INVALID;
47851         }
47852         this.form.afterAction(this, false);
47853     },
47854     failure : function(response)
47855     {
47856         this.uploadComplete= true;
47857         if (this.haveProgress) {
47858             Roo.MessageBox.hide();
47859         }
47860         
47861         this.response = response;
47862         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47863         this.form.afterAction(this, false);
47864     },
47865     
47866     handleResponse : function(response){
47867         if(this.form.errorReader){
47868             var rs = this.form.errorReader.read(response);
47869             var errors = [];
47870             if(rs.records){
47871                 for(var i = 0, len = rs.records.length; i < len; i++) {
47872                     var r = rs.records[i];
47873                     errors[i] = r.data;
47874                 }
47875             }
47876             if(errors.length < 1){
47877                 errors = null;
47878             }
47879             return {
47880                 success : rs.success,
47881                 errors : errors
47882             };
47883         }
47884         var ret = false;
47885         try {
47886             ret = Roo.decode(response.responseText);
47887         } catch (e) {
47888             ret = {
47889                 success: false,
47890                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47891                 errors : []
47892             };
47893         }
47894         return ret;
47895         
47896     }
47897 });
47898
47899
47900 Roo.form.Action.Load = function(form, options){
47901     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47902     this.reader = this.form.reader;
47903 };
47904
47905 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47906     type : 'load',
47907
47908     run : function(){
47909         
47910         Roo.Ajax.request(Roo.apply(
47911                 this.createCallback(), {
47912                     method:this.getMethod(),
47913                     url:this.getUrl(false),
47914                     params:this.getParams()
47915         }));
47916     },
47917
47918     success : function(response){
47919         
47920         var result = this.processResponse(response);
47921         if(result === true || !result.success || !result.data){
47922             this.failureType = Roo.form.Action.LOAD_FAILURE;
47923             this.form.afterAction(this, false);
47924             return;
47925         }
47926         this.form.clearInvalid();
47927         this.form.setValues(result.data);
47928         this.form.afterAction(this, true);
47929     },
47930
47931     handleResponse : function(response){
47932         if(this.form.reader){
47933             var rs = this.form.reader.read(response);
47934             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47935             return {
47936                 success : rs.success,
47937                 data : data
47938             };
47939         }
47940         return Roo.decode(response.responseText);
47941     }
47942 });
47943
47944 Roo.form.Action.ACTION_TYPES = {
47945     'load' : Roo.form.Action.Load,
47946     'submit' : Roo.form.Action.Submit
47947 };/*
47948  * Based on:
47949  * Ext JS Library 1.1.1
47950  * Copyright(c) 2006-2007, Ext JS, LLC.
47951  *
47952  * Originally Released Under LGPL - original licence link has changed is not relivant.
47953  *
47954  * Fork - LGPL
47955  * <script type="text/javascript">
47956  */
47957  
47958 /**
47959  * @class Roo.form.Layout
47960  * @extends Roo.Component
47961  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
47962  * @constructor
47963  * @param {Object} config Configuration options
47964  */
47965 Roo.form.Layout = function(config){
47966     var xitems = [];
47967     if (config.items) {
47968         xitems = config.items;
47969         delete config.items;
47970     }
47971     Roo.form.Layout.superclass.constructor.call(this, config);
47972     this.stack = [];
47973     Roo.each(xitems, this.addxtype, this);
47974      
47975 };
47976
47977 Roo.extend(Roo.form.Layout, Roo.Component, {
47978     /**
47979      * @cfg {String/Object} autoCreate
47980      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
47981      */
47982     /**
47983      * @cfg {String/Object/Function} style
47984      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
47985      * a function which returns such a specification.
47986      */
47987     /**
47988      * @cfg {String} labelAlign
47989      * Valid values are "left," "top" and "right" (defaults to "left")
47990      */
47991     /**
47992      * @cfg {Number} labelWidth
47993      * Fixed width in pixels of all field labels (defaults to undefined)
47994      */
47995     /**
47996      * @cfg {Boolean} clear
47997      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
47998      */
47999     clear : true,
48000     /**
48001      * @cfg {String} labelSeparator
48002      * The separator to use after field labels (defaults to ':')
48003      */
48004     labelSeparator : ':',
48005     /**
48006      * @cfg {Boolean} hideLabels
48007      * True to suppress the display of field labels in this layout (defaults to false)
48008      */
48009     hideLabels : false,
48010
48011     // private
48012     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48013     
48014     isLayout : true,
48015     
48016     // private
48017     onRender : function(ct, position){
48018         if(this.el){ // from markup
48019             this.el = Roo.get(this.el);
48020         }else {  // generate
48021             var cfg = this.getAutoCreate();
48022             this.el = ct.createChild(cfg, position);
48023         }
48024         if(this.style){
48025             this.el.applyStyles(this.style);
48026         }
48027         if(this.labelAlign){
48028             this.el.addClass('x-form-label-'+this.labelAlign);
48029         }
48030         if(this.hideLabels){
48031             this.labelStyle = "display:none";
48032             this.elementStyle = "padding-left:0;";
48033         }else{
48034             if(typeof this.labelWidth == 'number'){
48035                 this.labelStyle = "width:"+this.labelWidth+"px;";
48036                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48037             }
48038             if(this.labelAlign == 'top'){
48039                 this.labelStyle = "width:auto;";
48040                 this.elementStyle = "padding-left:0;";
48041             }
48042         }
48043         var stack = this.stack;
48044         var slen = stack.length;
48045         if(slen > 0){
48046             if(!this.fieldTpl){
48047                 var t = new Roo.Template(
48048                     '<div class="x-form-item {5}">',
48049                         '<label for="{0}" style="{2}">{1}{4}</label>',
48050                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48051                         '</div>',
48052                     '</div><div class="x-form-clear-left"></div>'
48053                 );
48054                 t.disableFormats = true;
48055                 t.compile();
48056                 Roo.form.Layout.prototype.fieldTpl = t;
48057             }
48058             for(var i = 0; i < slen; i++) {
48059                 if(stack[i].isFormField){
48060                     this.renderField(stack[i]);
48061                 }else{
48062                     this.renderComponent(stack[i]);
48063                 }
48064             }
48065         }
48066         if(this.clear){
48067             this.el.createChild({cls:'x-form-clear'});
48068         }
48069     },
48070
48071     // private
48072     renderField : function(f){
48073         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48074                f.id, //0
48075                f.fieldLabel, //1
48076                f.labelStyle||this.labelStyle||'', //2
48077                this.elementStyle||'', //3
48078                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48079                f.itemCls||this.itemCls||''  //5
48080        ], true).getPrevSibling());
48081     },
48082
48083     // private
48084     renderComponent : function(c){
48085         c.render(c.isLayout ? this.el : this.el.createChild());    
48086     },
48087     /**
48088      * Adds a object form elements (using the xtype property as the factory method.)
48089      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48090      * @param {Object} config 
48091      */
48092     addxtype : function(o)
48093     {
48094         // create the lement.
48095         o.form = this.form;
48096         var fe = Roo.factory(o, Roo.form);
48097         this.form.allItems.push(fe);
48098         this.stack.push(fe);
48099         
48100         if (fe.isFormField) {
48101             this.form.items.add(fe);
48102         }
48103          
48104         return fe;
48105     }
48106 });
48107
48108 /**
48109  * @class Roo.form.Column
48110  * @extends Roo.form.Layout
48111  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48112  * @constructor
48113  * @param {Object} config Configuration options
48114  */
48115 Roo.form.Column = function(config){
48116     Roo.form.Column.superclass.constructor.call(this, config);
48117 };
48118
48119 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48120     /**
48121      * @cfg {Number/String} width
48122      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48123      */
48124     /**
48125      * @cfg {String/Object} autoCreate
48126      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48127      */
48128
48129     // private
48130     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48131
48132     // private
48133     onRender : function(ct, position){
48134         Roo.form.Column.superclass.onRender.call(this, ct, position);
48135         if(this.width){
48136             this.el.setWidth(this.width);
48137         }
48138     }
48139 });
48140
48141
48142 /**
48143  * @class Roo.form.Row
48144  * @extends Roo.form.Layout
48145  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48146  * @constructor
48147  * @param {Object} config Configuration options
48148  */
48149
48150  
48151 Roo.form.Row = function(config){
48152     Roo.form.Row.superclass.constructor.call(this, config);
48153 };
48154  
48155 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48156       /**
48157      * @cfg {Number/String} width
48158      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48159      */
48160     /**
48161      * @cfg {Number/String} height
48162      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48163      */
48164     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48165     
48166     padWidth : 20,
48167     // private
48168     onRender : function(ct, position){
48169         //console.log('row render');
48170         if(!this.rowTpl){
48171             var t = new Roo.Template(
48172                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48173                     '<label for="{0}" style="{2}">{1}{4}</label>',
48174                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48175                     '</div>',
48176                 '</div>'
48177             );
48178             t.disableFormats = true;
48179             t.compile();
48180             Roo.form.Layout.prototype.rowTpl = t;
48181         }
48182         this.fieldTpl = this.rowTpl;
48183         
48184         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48185         var labelWidth = 100;
48186         
48187         if ((this.labelAlign != 'top')) {
48188             if (typeof this.labelWidth == 'number') {
48189                 labelWidth = this.labelWidth
48190             }
48191             this.padWidth =  20 + labelWidth;
48192             
48193         }
48194         
48195         Roo.form.Column.superclass.onRender.call(this, ct, position);
48196         if(this.width){
48197             this.el.setWidth(this.width);
48198         }
48199         if(this.height){
48200             this.el.setHeight(this.height);
48201         }
48202     },
48203     
48204     // private
48205     renderField : function(f){
48206         f.fieldEl = this.fieldTpl.append(this.el, [
48207                f.id, f.fieldLabel,
48208                f.labelStyle||this.labelStyle||'',
48209                this.elementStyle||'',
48210                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48211                f.itemCls||this.itemCls||'',
48212                f.width ? f.width + this.padWidth : 160 + this.padWidth
48213        ],true);
48214     }
48215 });
48216  
48217
48218 /**
48219  * @class Roo.form.FieldSet
48220  * @extends Roo.form.Layout
48221  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48222  * @constructor
48223  * @param {Object} config Configuration options
48224  */
48225 Roo.form.FieldSet = function(config){
48226     Roo.form.FieldSet.superclass.constructor.call(this, config);
48227 };
48228
48229 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48230     /**
48231      * @cfg {String} legend
48232      * The text to display as the legend for the FieldSet (defaults to '')
48233      */
48234     /**
48235      * @cfg {String/Object} autoCreate
48236      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48237      */
48238
48239     // private
48240     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48241
48242     // private
48243     onRender : function(ct, position){
48244         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48245         if(this.legend){
48246             this.setLegend(this.legend);
48247         }
48248     },
48249
48250     // private
48251     setLegend : function(text){
48252         if(this.rendered){
48253             this.el.child('legend').update(text);
48254         }
48255     }
48256 });/*
48257  * Based on:
48258  * Ext JS Library 1.1.1
48259  * Copyright(c) 2006-2007, Ext JS, LLC.
48260  *
48261  * Originally Released Under LGPL - original licence link has changed is not relivant.
48262  *
48263  * Fork - LGPL
48264  * <script type="text/javascript">
48265  */
48266 /**
48267  * @class Roo.form.VTypes
48268  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48269  * @singleton
48270  */
48271 Roo.form.VTypes = function(){
48272     // closure these in so they are only created once.
48273     var alpha = /^[a-zA-Z_]+$/;
48274     var alphanum = /^[a-zA-Z0-9_]+$/;
48275     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48276     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48277
48278     // All these messages and functions are configurable
48279     return {
48280         /**
48281          * The function used to validate email addresses
48282          * @param {String} value The email address
48283          */
48284         'email' : function(v){
48285             return email.test(v);
48286         },
48287         /**
48288          * The error text to display when the email validation function returns false
48289          * @type String
48290          */
48291         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48292         /**
48293          * The keystroke filter mask to be applied on email input
48294          * @type RegExp
48295          */
48296         'emailMask' : /[a-z0-9_\.\-@]/i,
48297
48298         /**
48299          * The function used to validate URLs
48300          * @param {String} value The URL
48301          */
48302         'url' : function(v){
48303             return url.test(v);
48304         },
48305         /**
48306          * The error text to display when the url validation function returns false
48307          * @type String
48308          */
48309         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48310         
48311         /**
48312          * The function used to validate alpha values
48313          * @param {String} value The value
48314          */
48315         'alpha' : function(v){
48316             return alpha.test(v);
48317         },
48318         /**
48319          * The error text to display when the alpha validation function returns false
48320          * @type String
48321          */
48322         'alphaText' : 'This field should only contain letters and _',
48323         /**
48324          * The keystroke filter mask to be applied on alpha input
48325          * @type RegExp
48326          */
48327         'alphaMask' : /[a-z_]/i,
48328
48329         /**
48330          * The function used to validate alphanumeric values
48331          * @param {String} value The value
48332          */
48333         'alphanum' : function(v){
48334             return alphanum.test(v);
48335         },
48336         /**
48337          * The error text to display when the alphanumeric validation function returns false
48338          * @type String
48339          */
48340         'alphanumText' : 'This field should only contain letters, numbers and _',
48341         /**
48342          * The keystroke filter mask to be applied on alphanumeric input
48343          * @type RegExp
48344          */
48345         'alphanumMask' : /[a-z0-9_]/i
48346     };
48347 }();//<script type="text/javascript">
48348
48349 /**
48350  * @class Roo.form.FCKeditor
48351  * @extends Roo.form.TextArea
48352  * Wrapper around the FCKEditor http://www.fckeditor.net
48353  * @constructor
48354  * Creates a new FCKeditor
48355  * @param {Object} config Configuration options
48356  */
48357 Roo.form.FCKeditor = function(config){
48358     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48359     this.addEvents({
48360          /**
48361          * @event editorinit
48362          * Fired when the editor is initialized - you can add extra handlers here..
48363          * @param {FCKeditor} this
48364          * @param {Object} the FCK object.
48365          */
48366         editorinit : true
48367     });
48368     
48369     
48370 };
48371 Roo.form.FCKeditor.editors = { };
48372 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48373 {
48374     //defaultAutoCreate : {
48375     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48376     //},
48377     // private
48378     /**
48379      * @cfg {Object} fck options - see fck manual for details.
48380      */
48381     fckconfig : false,
48382     
48383     /**
48384      * @cfg {Object} fck toolbar set (Basic or Default)
48385      */
48386     toolbarSet : 'Basic',
48387     /**
48388      * @cfg {Object} fck BasePath
48389      */ 
48390     basePath : '/fckeditor/',
48391     
48392     
48393     frame : false,
48394     
48395     value : '',
48396     
48397    
48398     onRender : function(ct, position)
48399     {
48400         if(!this.el){
48401             this.defaultAutoCreate = {
48402                 tag: "textarea",
48403                 style:"width:300px;height:60px;",
48404                 autocomplete: "new-password"
48405             };
48406         }
48407         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48408         /*
48409         if(this.grow){
48410             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48411             if(this.preventScrollbars){
48412                 this.el.setStyle("overflow", "hidden");
48413             }
48414             this.el.setHeight(this.growMin);
48415         }
48416         */
48417         //console.log('onrender' + this.getId() );
48418         Roo.form.FCKeditor.editors[this.getId()] = this;
48419          
48420
48421         this.replaceTextarea() ;
48422         
48423     },
48424     
48425     getEditor : function() {
48426         return this.fckEditor;
48427     },
48428     /**
48429      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48430      * @param {Mixed} value The value to set
48431      */
48432     
48433     
48434     setValue : function(value)
48435     {
48436         //console.log('setValue: ' + value);
48437         
48438         if(typeof(value) == 'undefined') { // not sure why this is happending...
48439             return;
48440         }
48441         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48442         
48443         //if(!this.el || !this.getEditor()) {
48444         //    this.value = value;
48445             //this.setValue.defer(100,this,[value]);    
48446         //    return;
48447         //} 
48448         
48449         if(!this.getEditor()) {
48450             return;
48451         }
48452         
48453         this.getEditor().SetData(value);
48454         
48455         //
48456
48457     },
48458
48459     /**
48460      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48461      * @return {Mixed} value The field value
48462      */
48463     getValue : function()
48464     {
48465         
48466         if (this.frame && this.frame.dom.style.display == 'none') {
48467             return Roo.form.FCKeditor.superclass.getValue.call(this);
48468         }
48469         
48470         if(!this.el || !this.getEditor()) {
48471            
48472            // this.getValue.defer(100,this); 
48473             return this.value;
48474         }
48475        
48476         
48477         var value=this.getEditor().GetData();
48478         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48479         return Roo.form.FCKeditor.superclass.getValue.call(this);
48480         
48481
48482     },
48483
48484     /**
48485      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48486      * @return {Mixed} value The field value
48487      */
48488     getRawValue : function()
48489     {
48490         if (this.frame && this.frame.dom.style.display == 'none') {
48491             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48492         }
48493         
48494         if(!this.el || !this.getEditor()) {
48495             //this.getRawValue.defer(100,this); 
48496             return this.value;
48497             return;
48498         }
48499         
48500         
48501         
48502         var value=this.getEditor().GetData();
48503         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48504         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48505          
48506     },
48507     
48508     setSize : function(w,h) {
48509         
48510         
48511         
48512         //if (this.frame && this.frame.dom.style.display == 'none') {
48513         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48514         //    return;
48515         //}
48516         //if(!this.el || !this.getEditor()) {
48517         //    this.setSize.defer(100,this, [w,h]); 
48518         //    return;
48519         //}
48520         
48521         
48522         
48523         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48524         
48525         this.frame.dom.setAttribute('width', w);
48526         this.frame.dom.setAttribute('height', h);
48527         this.frame.setSize(w,h);
48528         
48529     },
48530     
48531     toggleSourceEdit : function(value) {
48532         
48533       
48534          
48535         this.el.dom.style.display = value ? '' : 'none';
48536         this.frame.dom.style.display = value ?  'none' : '';
48537         
48538     },
48539     
48540     
48541     focus: function(tag)
48542     {
48543         if (this.frame.dom.style.display == 'none') {
48544             return Roo.form.FCKeditor.superclass.focus.call(this);
48545         }
48546         if(!this.el || !this.getEditor()) {
48547             this.focus.defer(100,this, [tag]); 
48548             return;
48549         }
48550         
48551         
48552         
48553         
48554         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48555         this.getEditor().Focus();
48556         if (tgs.length) {
48557             if (!this.getEditor().Selection.GetSelection()) {
48558                 this.focus.defer(100,this, [tag]); 
48559                 return;
48560             }
48561             
48562             
48563             var r = this.getEditor().EditorDocument.createRange();
48564             r.setStart(tgs[0],0);
48565             r.setEnd(tgs[0],0);
48566             this.getEditor().Selection.GetSelection().removeAllRanges();
48567             this.getEditor().Selection.GetSelection().addRange(r);
48568             this.getEditor().Focus();
48569         }
48570         
48571     },
48572     
48573     
48574     
48575     replaceTextarea : function()
48576     {
48577         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48578             return ;
48579         }
48580         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48581         //{
48582             // We must check the elements firstly using the Id and then the name.
48583         var oTextarea = document.getElementById( this.getId() );
48584         
48585         var colElementsByName = document.getElementsByName( this.getId() ) ;
48586          
48587         oTextarea.style.display = 'none' ;
48588
48589         if ( oTextarea.tabIndex ) {            
48590             this.TabIndex = oTextarea.tabIndex ;
48591         }
48592         
48593         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48594         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48595         this.frame = Roo.get(this.getId() + '___Frame')
48596     },
48597     
48598     _getConfigHtml : function()
48599     {
48600         var sConfig = '' ;
48601
48602         for ( var o in this.fckconfig ) {
48603             sConfig += sConfig.length > 0  ? '&amp;' : '';
48604             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48605         }
48606
48607         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48608     },
48609     
48610     
48611     _getIFrameHtml : function()
48612     {
48613         var sFile = 'fckeditor.html' ;
48614         /* no idea what this is about..
48615         try
48616         {
48617             if ( (/fcksource=true/i).test( window.top.location.search ) )
48618                 sFile = 'fckeditor.original.html' ;
48619         }
48620         catch (e) { 
48621         */
48622
48623         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48624         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48625         
48626         
48627         var html = '<iframe id="' + this.getId() +
48628             '___Frame" src="' + sLink +
48629             '" width="' + this.width +
48630             '" height="' + this.height + '"' +
48631             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48632             ' frameborder="0" scrolling="no"></iframe>' ;
48633
48634         return html ;
48635     },
48636     
48637     _insertHtmlBefore : function( html, element )
48638     {
48639         if ( element.insertAdjacentHTML )       {
48640             // IE
48641             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48642         } else { // Gecko
48643             var oRange = document.createRange() ;
48644             oRange.setStartBefore( element ) ;
48645             var oFragment = oRange.createContextualFragment( html );
48646             element.parentNode.insertBefore( oFragment, element ) ;
48647         }
48648     }
48649     
48650     
48651   
48652     
48653     
48654     
48655     
48656
48657 });
48658
48659 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48660
48661 function FCKeditor_OnComplete(editorInstance){
48662     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48663     f.fckEditor = editorInstance;
48664     //console.log("loaded");
48665     f.fireEvent('editorinit', f, editorInstance);
48666
48667   
48668
48669  
48670
48671
48672
48673
48674
48675
48676
48677
48678
48679
48680
48681
48682
48683
48684
48685 //<script type="text/javascript">
48686 /**
48687  * @class Roo.form.GridField
48688  * @extends Roo.form.Field
48689  * Embed a grid (or editable grid into a form)
48690  * STATUS ALPHA
48691  * 
48692  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48693  * it needs 
48694  * xgrid.store = Roo.data.Store
48695  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48696  * xgrid.store.reader = Roo.data.JsonReader 
48697  * 
48698  * 
48699  * @constructor
48700  * Creates a new GridField
48701  * @param {Object} config Configuration options
48702  */
48703 Roo.form.GridField = function(config){
48704     Roo.form.GridField.superclass.constructor.call(this, config);
48705      
48706 };
48707
48708 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48709     /**
48710      * @cfg {Number} width  - used to restrict width of grid..
48711      */
48712     width : 100,
48713     /**
48714      * @cfg {Number} height - used to restrict height of grid..
48715      */
48716     height : 50,
48717      /**
48718      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48719          * 
48720          *}
48721      */
48722     xgrid : false, 
48723     /**
48724      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48725      * {tag: "input", type: "checkbox", autocomplete: "off"})
48726      */
48727    // defaultAutoCreate : { tag: 'div' },
48728     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48729     /**
48730      * @cfg {String} addTitle Text to include for adding a title.
48731      */
48732     addTitle : false,
48733     //
48734     onResize : function(){
48735         Roo.form.Field.superclass.onResize.apply(this, arguments);
48736     },
48737
48738     initEvents : function(){
48739         // Roo.form.Checkbox.superclass.initEvents.call(this);
48740         // has no events...
48741        
48742     },
48743
48744
48745     getResizeEl : function(){
48746         return this.wrap;
48747     },
48748
48749     getPositionEl : function(){
48750         return this.wrap;
48751     },
48752
48753     // private
48754     onRender : function(ct, position){
48755         
48756         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48757         var style = this.style;
48758         delete this.style;
48759         
48760         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48761         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48762         this.viewEl = this.wrap.createChild({ tag: 'div' });
48763         if (style) {
48764             this.viewEl.applyStyles(style);
48765         }
48766         if (this.width) {
48767             this.viewEl.setWidth(this.width);
48768         }
48769         if (this.height) {
48770             this.viewEl.setHeight(this.height);
48771         }
48772         //if(this.inputValue !== undefined){
48773         //this.setValue(this.value);
48774         
48775         
48776         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48777         
48778         
48779         this.grid.render();
48780         this.grid.getDataSource().on('remove', this.refreshValue, this);
48781         this.grid.getDataSource().on('update', this.refreshValue, this);
48782         this.grid.on('afteredit', this.refreshValue, this);
48783  
48784     },
48785      
48786     
48787     /**
48788      * Sets the value of the item. 
48789      * @param {String} either an object  or a string..
48790      */
48791     setValue : function(v){
48792         //this.value = v;
48793         v = v || []; // empty set..
48794         // this does not seem smart - it really only affects memoryproxy grids..
48795         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48796             var ds = this.grid.getDataSource();
48797             // assumes a json reader..
48798             var data = {}
48799             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48800             ds.loadData( data);
48801         }
48802         // clear selection so it does not get stale.
48803         if (this.grid.sm) { 
48804             this.grid.sm.clearSelections();
48805         }
48806         
48807         Roo.form.GridField.superclass.setValue.call(this, v);
48808         this.refreshValue();
48809         // should load data in the grid really....
48810     },
48811     
48812     // private
48813     refreshValue: function() {
48814          var val = [];
48815         this.grid.getDataSource().each(function(r) {
48816             val.push(r.data);
48817         });
48818         this.el.dom.value = Roo.encode(val);
48819     }
48820     
48821      
48822     
48823     
48824 });/*
48825  * Based on:
48826  * Ext JS Library 1.1.1
48827  * Copyright(c) 2006-2007, Ext JS, LLC.
48828  *
48829  * Originally Released Under LGPL - original licence link has changed is not relivant.
48830  *
48831  * Fork - LGPL
48832  * <script type="text/javascript">
48833  */
48834 /**
48835  * @class Roo.form.DisplayField
48836  * @extends Roo.form.Field
48837  * A generic Field to display non-editable data.
48838  * @cfg {Boolean} closable (true|false) default false
48839  * @constructor
48840  * Creates a new Display Field item.
48841  * @param {Object} config Configuration options
48842  */
48843 Roo.form.DisplayField = function(config){
48844     Roo.form.DisplayField.superclass.constructor.call(this, config);
48845     
48846     this.addEvents({
48847         /**
48848          * @event close
48849          * Fires after the click the close btn
48850              * @param {Roo.form.DisplayField} this
48851              */
48852         close : true
48853     });
48854 };
48855
48856 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48857     inputType:      'hidden',
48858     allowBlank:     true,
48859     readOnly:         true,
48860     
48861  
48862     /**
48863      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48864      */
48865     focusClass : undefined,
48866     /**
48867      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48868      */
48869     fieldClass: 'x-form-field',
48870     
48871      /**
48872      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48873      */
48874     valueRenderer: undefined,
48875     
48876     width: 100,
48877     /**
48878      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48879      * {tag: "input", type: "checkbox", autocomplete: "off"})
48880      */
48881      
48882  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48883  
48884     closable : false,
48885     
48886     onResize : function(){
48887         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48888         
48889     },
48890
48891     initEvents : function(){
48892         // Roo.form.Checkbox.superclass.initEvents.call(this);
48893         // has no events...
48894         
48895         if(this.closable){
48896             this.closeEl.on('click', this.onClose, this);
48897         }
48898        
48899     },
48900
48901
48902     getResizeEl : function(){
48903         return this.wrap;
48904     },
48905
48906     getPositionEl : function(){
48907         return this.wrap;
48908     },
48909
48910     // private
48911     onRender : function(ct, position){
48912         
48913         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48914         //if(this.inputValue !== undefined){
48915         this.wrap = this.el.wrap();
48916         
48917         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48918         
48919         if(this.closable){
48920             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48921         }
48922         
48923         if (this.bodyStyle) {
48924             this.viewEl.applyStyles(this.bodyStyle);
48925         }
48926         //this.viewEl.setStyle('padding', '2px');
48927         
48928         this.setValue(this.value);
48929         
48930     },
48931 /*
48932     // private
48933     initValue : Roo.emptyFn,
48934
48935   */
48936
48937         // private
48938     onClick : function(){
48939         
48940     },
48941
48942     /**
48943      * Sets the checked state of the checkbox.
48944      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
48945      */
48946     setValue : function(v){
48947         this.value = v;
48948         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
48949         // this might be called before we have a dom element..
48950         if (!this.viewEl) {
48951             return;
48952         }
48953         this.viewEl.dom.innerHTML = html;
48954         Roo.form.DisplayField.superclass.setValue.call(this, v);
48955
48956     },
48957     
48958     onClose : function(e)
48959     {
48960         e.preventDefault();
48961         
48962         this.fireEvent('close', this);
48963     }
48964 });/*
48965  * 
48966  * Licence- LGPL
48967  * 
48968  */
48969
48970 /**
48971  * @class Roo.form.DayPicker
48972  * @extends Roo.form.Field
48973  * A Day picker show [M] [T] [W] ....
48974  * @constructor
48975  * Creates a new Day Picker
48976  * @param {Object} config Configuration options
48977  */
48978 Roo.form.DayPicker= function(config){
48979     Roo.form.DayPicker.superclass.constructor.call(this, config);
48980      
48981 };
48982
48983 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
48984     /**
48985      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48986      */
48987     focusClass : undefined,
48988     /**
48989      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48990      */
48991     fieldClass: "x-form-field",
48992    
48993     /**
48994      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48995      * {tag: "input", type: "checkbox", autocomplete: "off"})
48996      */
48997     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
48998     
48999    
49000     actionMode : 'viewEl', 
49001     //
49002     // private
49003  
49004     inputType : 'hidden',
49005     
49006      
49007     inputElement: false, // real input element?
49008     basedOn: false, // ????
49009     
49010     isFormField: true, // not sure where this is needed!!!!
49011
49012     onResize : function(){
49013         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49014         if(!this.boxLabel){
49015             this.el.alignTo(this.wrap, 'c-c');
49016         }
49017     },
49018
49019     initEvents : function(){
49020         Roo.form.Checkbox.superclass.initEvents.call(this);
49021         this.el.on("click", this.onClick,  this);
49022         this.el.on("change", this.onClick,  this);
49023     },
49024
49025
49026     getResizeEl : function(){
49027         return this.wrap;
49028     },
49029
49030     getPositionEl : function(){
49031         return this.wrap;
49032     },
49033
49034     
49035     // private
49036     onRender : function(ct, position){
49037         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49038        
49039         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49040         
49041         var r1 = '<table><tr>';
49042         var r2 = '<tr class="x-form-daypick-icons">';
49043         for (var i=0; i < 7; i++) {
49044             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49045             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49046         }
49047         
49048         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49049         viewEl.select('img').on('click', this.onClick, this);
49050         this.viewEl = viewEl;   
49051         
49052         
49053         // this will not work on Chrome!!!
49054         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49055         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49056         
49057         
49058           
49059
49060     },
49061
49062     // private
49063     initValue : Roo.emptyFn,
49064
49065     /**
49066      * Returns the checked state of the checkbox.
49067      * @return {Boolean} True if checked, else false
49068      */
49069     getValue : function(){
49070         return this.el.dom.value;
49071         
49072     },
49073
49074         // private
49075     onClick : function(e){ 
49076         //this.setChecked(!this.checked);
49077         Roo.get(e.target).toggleClass('x-menu-item-checked');
49078         this.refreshValue();
49079         //if(this.el.dom.checked != this.checked){
49080         //    this.setValue(this.el.dom.checked);
49081        // }
49082     },
49083     
49084     // private
49085     refreshValue : function()
49086     {
49087         var val = '';
49088         this.viewEl.select('img',true).each(function(e,i,n)  {
49089             val += e.is(".x-menu-item-checked") ? String(n) : '';
49090         });
49091         this.setValue(val, true);
49092     },
49093
49094     /**
49095      * Sets the checked state of the checkbox.
49096      * On is always based on a string comparison between inputValue and the param.
49097      * @param {Boolean/String} value - the value to set 
49098      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49099      */
49100     setValue : function(v,suppressEvent){
49101         if (!this.el.dom) {
49102             return;
49103         }
49104         var old = this.el.dom.value ;
49105         this.el.dom.value = v;
49106         if (suppressEvent) {
49107             return ;
49108         }
49109          
49110         // update display..
49111         this.viewEl.select('img',true).each(function(e,i,n)  {
49112             
49113             var on = e.is(".x-menu-item-checked");
49114             var newv = v.indexOf(String(n)) > -1;
49115             if (on != newv) {
49116                 e.toggleClass('x-menu-item-checked');
49117             }
49118             
49119         });
49120         
49121         
49122         this.fireEvent('change', this, v, old);
49123         
49124         
49125     },
49126    
49127     // handle setting of hidden value by some other method!!?!?
49128     setFromHidden: function()
49129     {
49130         if(!this.el){
49131             return;
49132         }
49133         //console.log("SET FROM HIDDEN");
49134         //alert('setFrom hidden');
49135         this.setValue(this.el.dom.value);
49136     },
49137     
49138     onDestroy : function()
49139     {
49140         if(this.viewEl){
49141             Roo.get(this.viewEl).remove();
49142         }
49143          
49144         Roo.form.DayPicker.superclass.onDestroy.call(this);
49145     }
49146
49147 });/*
49148  * RooJS Library 1.1.1
49149  * Copyright(c) 2008-2011  Alan Knowles
49150  *
49151  * License - LGPL
49152  */
49153  
49154
49155 /**
49156  * @class Roo.form.ComboCheck
49157  * @extends Roo.form.ComboBox
49158  * A combobox for multiple select items.
49159  *
49160  * FIXME - could do with a reset button..
49161  * 
49162  * @constructor
49163  * Create a new ComboCheck
49164  * @param {Object} config Configuration options
49165  */
49166 Roo.form.ComboCheck = function(config){
49167     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49168     // should verify some data...
49169     // like
49170     // hiddenName = required..
49171     // displayField = required
49172     // valudField == required
49173     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49174     var _t = this;
49175     Roo.each(req, function(e) {
49176         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49177             throw "Roo.form.ComboCheck : missing value for: " + e;
49178         }
49179     });
49180     
49181     
49182 };
49183
49184 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49185      
49186      
49187     editable : false,
49188      
49189     selectedClass: 'x-menu-item-checked', 
49190     
49191     // private
49192     onRender : function(ct, position){
49193         var _t = this;
49194         
49195         
49196         
49197         if(!this.tpl){
49198             var cls = 'x-combo-list';
49199
49200             
49201             this.tpl =  new Roo.Template({
49202                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49203                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49204                    '<span>{' + this.displayField + '}</span>' +
49205                     '</div>' 
49206                 
49207             });
49208         }
49209  
49210         
49211         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49212         this.view.singleSelect = false;
49213         this.view.multiSelect = true;
49214         this.view.toggleSelect = true;
49215         this.pageTb.add(new Roo.Toolbar.Fill(), {
49216             
49217             text: 'Done',
49218             handler: function()
49219             {
49220                 _t.collapse();
49221             }
49222         });
49223     },
49224     
49225     onViewOver : function(e, t){
49226         // do nothing...
49227         return;
49228         
49229     },
49230     
49231     onViewClick : function(doFocus,index){
49232         return;
49233         
49234     },
49235     select: function () {
49236         //Roo.log("SELECT CALLED");
49237     },
49238      
49239     selectByValue : function(xv, scrollIntoView){
49240         var ar = this.getValueArray();
49241         var sels = [];
49242         
49243         Roo.each(ar, function(v) {
49244             if(v === undefined || v === null){
49245                 return;
49246             }
49247             var r = this.findRecord(this.valueField, v);
49248             if(r){
49249                 sels.push(this.store.indexOf(r))
49250                 
49251             }
49252         },this);
49253         this.view.select(sels);
49254         return false;
49255     },
49256     
49257     
49258     
49259     onSelect : function(record, index){
49260        // Roo.log("onselect Called");
49261        // this is only called by the clear button now..
49262         this.view.clearSelections();
49263         this.setValue('[]');
49264         if (this.value != this.valueBefore) {
49265             this.fireEvent('change', this, this.value, this.valueBefore);
49266             this.valueBefore = this.value;
49267         }
49268     },
49269     getValueArray : function()
49270     {
49271         var ar = [] ;
49272         
49273         try {
49274             //Roo.log(this.value);
49275             if (typeof(this.value) == 'undefined') {
49276                 return [];
49277             }
49278             var ar = Roo.decode(this.value);
49279             return  ar instanceof Array ? ar : []; //?? valid?
49280             
49281         } catch(e) {
49282             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49283             return [];
49284         }
49285          
49286     },
49287     expand : function ()
49288     {
49289         
49290         Roo.form.ComboCheck.superclass.expand.call(this);
49291         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49292         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49293         
49294
49295     },
49296     
49297     collapse : function(){
49298         Roo.form.ComboCheck.superclass.collapse.call(this);
49299         var sl = this.view.getSelectedIndexes();
49300         var st = this.store;
49301         var nv = [];
49302         var tv = [];
49303         var r;
49304         Roo.each(sl, function(i) {
49305             r = st.getAt(i);
49306             nv.push(r.get(this.valueField));
49307         },this);
49308         this.setValue(Roo.encode(nv));
49309         if (this.value != this.valueBefore) {
49310
49311             this.fireEvent('change', this, this.value, this.valueBefore);
49312             this.valueBefore = this.value;
49313         }
49314         
49315     },
49316     
49317     setValue : function(v){
49318         // Roo.log(v);
49319         this.value = v;
49320         
49321         var vals = this.getValueArray();
49322         var tv = [];
49323         Roo.each(vals, function(k) {
49324             var r = this.findRecord(this.valueField, k);
49325             if(r){
49326                 tv.push(r.data[this.displayField]);
49327             }else if(this.valueNotFoundText !== undefined){
49328                 tv.push( this.valueNotFoundText );
49329             }
49330         },this);
49331        // Roo.log(tv);
49332         
49333         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49334         this.hiddenField.value = v;
49335         this.value = v;
49336     }
49337     
49338 });/*
49339  * Based on:
49340  * Ext JS Library 1.1.1
49341  * Copyright(c) 2006-2007, Ext JS, LLC.
49342  *
49343  * Originally Released Under LGPL - original licence link has changed is not relivant.
49344  *
49345  * Fork - LGPL
49346  * <script type="text/javascript">
49347  */
49348  
49349 /**
49350  * @class Roo.form.Signature
49351  * @extends Roo.form.Field
49352  * Signature field.  
49353  * @constructor
49354  * 
49355  * @param {Object} config Configuration options
49356  */
49357
49358 Roo.form.Signature = function(config){
49359     Roo.form.Signature.superclass.constructor.call(this, config);
49360     
49361     this.addEvents({// not in used??
49362          /**
49363          * @event confirm
49364          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49365              * @param {Roo.form.Signature} combo This combo box
49366              */
49367         'confirm' : true,
49368         /**
49369          * @event reset
49370          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49371              * @param {Roo.form.ComboBox} combo This combo box
49372              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49373              */
49374         'reset' : true
49375     });
49376 };
49377
49378 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49379     /**
49380      * @cfg {Object} labels Label to use when rendering a form.
49381      * defaults to 
49382      * labels : { 
49383      *      clear : "Clear",
49384      *      confirm : "Confirm"
49385      *  }
49386      */
49387     labels : { 
49388         clear : "Clear",
49389         confirm : "Confirm"
49390     },
49391     /**
49392      * @cfg {Number} width The signature panel width (defaults to 300)
49393      */
49394     width: 300,
49395     /**
49396      * @cfg {Number} height The signature panel height (defaults to 100)
49397      */
49398     height : 100,
49399     /**
49400      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49401      */
49402     allowBlank : false,
49403     
49404     //private
49405     // {Object} signPanel The signature SVG panel element (defaults to {})
49406     signPanel : {},
49407     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49408     isMouseDown : false,
49409     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49410     isConfirmed : false,
49411     // {String} signatureTmp SVG mapping string (defaults to empty string)
49412     signatureTmp : '',
49413     
49414     
49415     defaultAutoCreate : { // modified by initCompnoent..
49416         tag: "input",
49417         type:"hidden"
49418     },
49419
49420     // private
49421     onRender : function(ct, position){
49422         
49423         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49424         
49425         this.wrap = this.el.wrap({
49426             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49427         });
49428         
49429         this.createToolbar(this);
49430         this.signPanel = this.wrap.createChild({
49431                 tag: 'div',
49432                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49433             }, this.el
49434         );
49435             
49436         this.svgID = Roo.id();
49437         this.svgEl = this.signPanel.createChild({
49438               xmlns : 'http://www.w3.org/2000/svg',
49439               tag : 'svg',
49440               id : this.svgID + "-svg",
49441               width: this.width,
49442               height: this.height,
49443               viewBox: '0 0 '+this.width+' '+this.height,
49444               cn : [
49445                 {
49446                     tag: "rect",
49447                     id: this.svgID + "-svg-r",
49448                     width: this.width,
49449                     height: this.height,
49450                     fill: "#ffa"
49451                 },
49452                 {
49453                     tag: "line",
49454                     id: this.svgID + "-svg-l",
49455                     x1: "0", // start
49456                     y1: (this.height*0.8), // start set the line in 80% of height
49457                     x2: this.width, // end
49458                     y2: (this.height*0.8), // end set the line in 80% of height
49459                     'stroke': "#666",
49460                     'stroke-width': "1",
49461                     'stroke-dasharray': "3",
49462                     'shape-rendering': "crispEdges",
49463                     'pointer-events': "none"
49464                 },
49465                 {
49466                     tag: "path",
49467                     id: this.svgID + "-svg-p",
49468                     'stroke': "navy",
49469                     'stroke-width': "3",
49470                     'fill': "none",
49471                     'pointer-events': 'none'
49472                 }
49473               ]
49474         });
49475         this.createSVG();
49476         this.svgBox = this.svgEl.dom.getScreenCTM();
49477     },
49478     createSVG : function(){ 
49479         var svg = this.signPanel;
49480         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49481         var t = this;
49482
49483         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49484         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49485         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49486         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49487         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49488         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49489         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49490         
49491     },
49492     isTouchEvent : function(e){
49493         return e.type.match(/^touch/);
49494     },
49495     getCoords : function (e) {
49496         var pt    = this.svgEl.dom.createSVGPoint();
49497         pt.x = e.clientX; 
49498         pt.y = e.clientY;
49499         if (this.isTouchEvent(e)) {
49500             pt.x =  e.targetTouches[0].clientX;
49501             pt.y = e.targetTouches[0].clientY;
49502         }
49503         var a = this.svgEl.dom.getScreenCTM();
49504         var b = a.inverse();
49505         var mx = pt.matrixTransform(b);
49506         return mx.x + ',' + mx.y;
49507     },
49508     //mouse event headler 
49509     down : function (e) {
49510         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49511         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49512         
49513         this.isMouseDown = true;
49514         
49515         e.preventDefault();
49516     },
49517     move : function (e) {
49518         if (this.isMouseDown) {
49519             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49520             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49521         }
49522         
49523         e.preventDefault();
49524     },
49525     up : function (e) {
49526         this.isMouseDown = false;
49527         var sp = this.signatureTmp.split(' ');
49528         
49529         if(sp.length > 1){
49530             if(!sp[sp.length-2].match(/^L/)){
49531                 sp.pop();
49532                 sp.pop();
49533                 sp.push("");
49534                 this.signatureTmp = sp.join(" ");
49535             }
49536         }
49537         if(this.getValue() != this.signatureTmp){
49538             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49539             this.isConfirmed = false;
49540         }
49541         e.preventDefault();
49542     },
49543     
49544     /**
49545      * Protected method that will not generally be called directly. It
49546      * is called when the editor creates its toolbar. Override this method if you need to
49547      * add custom toolbar buttons.
49548      * @param {HtmlEditor} editor
49549      */
49550     createToolbar : function(editor){
49551          function btn(id, toggle, handler){
49552             var xid = fid + '-'+ id ;
49553             return {
49554                 id : xid,
49555                 cmd : id,
49556                 cls : 'x-btn-icon x-edit-'+id,
49557                 enableToggle:toggle !== false,
49558                 scope: editor, // was editor...
49559                 handler:handler||editor.relayBtnCmd,
49560                 clickEvent:'mousedown',
49561                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49562                 tabIndex:-1
49563             };
49564         }
49565         
49566         
49567         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49568         this.tb = tb;
49569         this.tb.add(
49570            {
49571                 cls : ' x-signature-btn x-signature-'+id,
49572                 scope: editor, // was editor...
49573                 handler: this.reset,
49574                 clickEvent:'mousedown',
49575                 text: this.labels.clear
49576             },
49577             {
49578                  xtype : 'Fill',
49579                  xns: Roo.Toolbar
49580             }, 
49581             {
49582                 cls : '  x-signature-btn x-signature-'+id,
49583                 scope: editor, // was editor...
49584                 handler: this.confirmHandler,
49585                 clickEvent:'mousedown',
49586                 text: this.labels.confirm
49587             }
49588         );
49589     
49590     },
49591     //public
49592     /**
49593      * when user is clicked confirm then show this image.....
49594      * 
49595      * @return {String} Image Data URI
49596      */
49597     getImageDataURI : function(){
49598         var svg = this.svgEl.dom.parentNode.innerHTML;
49599         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49600         return src; 
49601     },
49602     /**
49603      * 
49604      * @return {Boolean} this.isConfirmed
49605      */
49606     getConfirmed : function(){
49607         return this.isConfirmed;
49608     },
49609     /**
49610      * 
49611      * @return {Number} this.width
49612      */
49613     getWidth : function(){
49614         return this.width;
49615     },
49616     /**
49617      * 
49618      * @return {Number} this.height
49619      */
49620     getHeight : function(){
49621         return this.height;
49622     },
49623     // private
49624     getSignature : function(){
49625         return this.signatureTmp;
49626     },
49627     // private
49628     reset : function(){
49629         this.signatureTmp = '';
49630         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49631         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49632         this.isConfirmed = false;
49633         Roo.form.Signature.superclass.reset.call(this);
49634     },
49635     setSignature : function(s){
49636         this.signatureTmp = s;
49637         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49638         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49639         this.setValue(s);
49640         this.isConfirmed = false;
49641         Roo.form.Signature.superclass.reset.call(this);
49642     }, 
49643     test : function(){
49644 //        Roo.log(this.signPanel.dom.contentWindow.up())
49645     },
49646     //private
49647     setConfirmed : function(){
49648         
49649         
49650         
49651 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49652     },
49653     // private
49654     confirmHandler : function(){
49655         if(!this.getSignature()){
49656             return;
49657         }
49658         
49659         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49660         this.setValue(this.getSignature());
49661         this.isConfirmed = true;
49662         
49663         this.fireEvent('confirm', this);
49664     },
49665     // private
49666     // Subclasses should provide the validation implementation by overriding this
49667     validateValue : function(value){
49668         if(this.allowBlank){
49669             return true;
49670         }
49671         
49672         if(this.isConfirmed){
49673             return true;
49674         }
49675         return false;
49676     }
49677 });/*
49678  * Based on:
49679  * Ext JS Library 1.1.1
49680  * Copyright(c) 2006-2007, Ext JS, LLC.
49681  *
49682  * Originally Released Under LGPL - original licence link has changed is not relivant.
49683  *
49684  * Fork - LGPL
49685  * <script type="text/javascript">
49686  */
49687  
49688
49689 /**
49690  * @class Roo.form.ComboBox
49691  * @extends Roo.form.TriggerField
49692  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49693  * @constructor
49694  * Create a new ComboBox.
49695  * @param {Object} config Configuration options
49696  */
49697 Roo.form.Select = function(config){
49698     Roo.form.Select.superclass.constructor.call(this, config);
49699      
49700 };
49701
49702 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49703     /**
49704      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49705      */
49706     /**
49707      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49708      * rendering into an Roo.Editor, defaults to false)
49709      */
49710     /**
49711      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49712      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49713      */
49714     /**
49715      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49716      */
49717     /**
49718      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49719      * the dropdown list (defaults to undefined, with no header element)
49720      */
49721
49722      /**
49723      * @cfg {String/Roo.Template} tpl The template to use to render the output
49724      */
49725      
49726     // private
49727     defaultAutoCreate : {tag: "select"  },
49728     /**
49729      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49730      */
49731     listWidth: undefined,
49732     /**
49733      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49734      * mode = 'remote' or 'text' if mode = 'local')
49735      */
49736     displayField: undefined,
49737     /**
49738      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49739      * mode = 'remote' or 'value' if mode = 'local'). 
49740      * Note: use of a valueField requires the user make a selection
49741      * in order for a value to be mapped.
49742      */
49743     valueField: undefined,
49744     
49745     
49746     /**
49747      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49748      * field's data value (defaults to the underlying DOM element's name)
49749      */
49750     hiddenName: undefined,
49751     /**
49752      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49753      */
49754     listClass: '',
49755     /**
49756      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49757      */
49758     selectedClass: 'x-combo-selected',
49759     /**
49760      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49761      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49762      * which displays a downward arrow icon).
49763      */
49764     triggerClass : 'x-form-arrow-trigger',
49765     /**
49766      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49767      */
49768     shadow:'sides',
49769     /**
49770      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49771      * anchor positions (defaults to 'tl-bl')
49772      */
49773     listAlign: 'tl-bl?',
49774     /**
49775      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49776      */
49777     maxHeight: 300,
49778     /**
49779      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49780      * query specified by the allQuery config option (defaults to 'query')
49781      */
49782     triggerAction: 'query',
49783     /**
49784      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49785      * (defaults to 4, does not apply if editable = false)
49786      */
49787     minChars : 4,
49788     /**
49789      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49790      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49791      */
49792     typeAhead: false,
49793     /**
49794      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49795      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49796      */
49797     queryDelay: 500,
49798     /**
49799      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49800      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49801      */
49802     pageSize: 0,
49803     /**
49804      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49805      * when editable = true (defaults to false)
49806      */
49807     selectOnFocus:false,
49808     /**
49809      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49810      */
49811     queryParam: 'query',
49812     /**
49813      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49814      * when mode = 'remote' (defaults to 'Loading...')
49815      */
49816     loadingText: 'Loading...',
49817     /**
49818      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49819      */
49820     resizable: false,
49821     /**
49822      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49823      */
49824     handleHeight : 8,
49825     /**
49826      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49827      * traditional select (defaults to true)
49828      */
49829     editable: true,
49830     /**
49831      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49832      */
49833     allQuery: '',
49834     /**
49835      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49836      */
49837     mode: 'remote',
49838     /**
49839      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49840      * listWidth has a higher value)
49841      */
49842     minListWidth : 70,
49843     /**
49844      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49845      * allow the user to set arbitrary text into the field (defaults to false)
49846      */
49847     forceSelection:false,
49848     /**
49849      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49850      * if typeAhead = true (defaults to 250)
49851      */
49852     typeAheadDelay : 250,
49853     /**
49854      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49855      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49856      */
49857     valueNotFoundText : undefined,
49858     
49859     /**
49860      * @cfg {String} defaultValue The value displayed after loading the store.
49861      */
49862     defaultValue: '',
49863     
49864     /**
49865      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49866      */
49867     blockFocus : false,
49868     
49869     /**
49870      * @cfg {Boolean} disableClear Disable showing of clear button.
49871      */
49872     disableClear : false,
49873     /**
49874      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49875      */
49876     alwaysQuery : false,
49877     
49878     //private
49879     addicon : false,
49880     editicon: false,
49881     
49882     // element that contains real text value.. (when hidden is used..)
49883      
49884     // private
49885     onRender : function(ct, position){
49886         Roo.form.Field.prototype.onRender.call(this, ct, position);
49887         
49888         if(this.store){
49889             this.store.on('beforeload', this.onBeforeLoad, this);
49890             this.store.on('load', this.onLoad, this);
49891             this.store.on('loadexception', this.onLoadException, this);
49892             this.store.load({});
49893         }
49894         
49895         
49896         
49897     },
49898
49899     // private
49900     initEvents : function(){
49901         //Roo.form.ComboBox.superclass.initEvents.call(this);
49902  
49903     },
49904
49905     onDestroy : function(){
49906        
49907         if(this.store){
49908             this.store.un('beforeload', this.onBeforeLoad, this);
49909             this.store.un('load', this.onLoad, this);
49910             this.store.un('loadexception', this.onLoadException, this);
49911         }
49912         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49913     },
49914
49915     // private
49916     fireKey : function(e){
49917         if(e.isNavKeyPress() && !this.list.isVisible()){
49918             this.fireEvent("specialkey", this, e);
49919         }
49920     },
49921
49922     // private
49923     onResize: function(w, h){
49924         
49925         return; 
49926     
49927         
49928     },
49929
49930     /**
49931      * Allow or prevent the user from directly editing the field text.  If false is passed,
49932      * the user will only be able to select from the items defined in the dropdown list.  This method
49933      * is the runtime equivalent of setting the 'editable' config option at config time.
49934      * @param {Boolean} value True to allow the user to directly edit the field text
49935      */
49936     setEditable : function(value){
49937          
49938     },
49939
49940     // private
49941     onBeforeLoad : function(){
49942         
49943         Roo.log("Select before load");
49944         return;
49945     
49946         this.innerList.update(this.loadingText ?
49947                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
49948         //this.restrictHeight();
49949         this.selectedIndex = -1;
49950     },
49951
49952     // private
49953     onLoad : function(){
49954
49955     
49956         var dom = this.el.dom;
49957         dom.innerHTML = '';
49958          var od = dom.ownerDocument;
49959          
49960         if (this.emptyText) {
49961             var op = od.createElement('option');
49962             op.setAttribute('value', '');
49963             op.innerHTML = String.format('{0}', this.emptyText);
49964             dom.appendChild(op);
49965         }
49966         if(this.store.getCount() > 0){
49967            
49968             var vf = this.valueField;
49969             var df = this.displayField;
49970             this.store.data.each(function(r) {
49971                 // which colmsn to use... testing - cdoe / title..
49972                 var op = od.createElement('option');
49973                 op.setAttribute('value', r.data[vf]);
49974                 op.innerHTML = String.format('{0}', r.data[df]);
49975                 dom.appendChild(op);
49976             });
49977             if (typeof(this.defaultValue != 'undefined')) {
49978                 this.setValue(this.defaultValue);
49979             }
49980             
49981              
49982         }else{
49983             //this.onEmptyResults();
49984         }
49985         //this.el.focus();
49986     },
49987     // private
49988     onLoadException : function()
49989     {
49990         dom.innerHTML = '';
49991             
49992         Roo.log("Select on load exception");
49993         return;
49994     
49995         this.collapse();
49996         Roo.log(this.store.reader.jsonData);
49997         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
49998             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
49999         }
50000         
50001         
50002     },
50003     // private
50004     onTypeAhead : function(){
50005          
50006     },
50007
50008     // private
50009     onSelect : function(record, index){
50010         Roo.log('on select?');
50011         return;
50012         if(this.fireEvent('beforeselect', this, record, index) !== false){
50013             this.setFromData(index > -1 ? record.data : false);
50014             this.collapse();
50015             this.fireEvent('select', this, record, index);
50016         }
50017     },
50018
50019     /**
50020      * Returns the currently selected field value or empty string if no value is set.
50021      * @return {String} value The selected value
50022      */
50023     getValue : function(){
50024         var dom = this.el.dom;
50025         this.value = dom.options[dom.selectedIndex].value;
50026         return this.value;
50027         
50028     },
50029
50030     /**
50031      * Clears any text/value currently set in the field
50032      */
50033     clearValue : function(){
50034         this.value = '';
50035         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50036         
50037     },
50038
50039     /**
50040      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50041      * will be displayed in the field.  If the value does not match the data value of an existing item,
50042      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50043      * Otherwise the field will be blank (although the value will still be set).
50044      * @param {String} value The value to match
50045      */
50046     setValue : function(v){
50047         var d = this.el.dom;
50048         for (var i =0; i < d.options.length;i++) {
50049             if (v == d.options[i].value) {
50050                 d.selectedIndex = i;
50051                 this.value = v;
50052                 return;
50053             }
50054         }
50055         this.clearValue();
50056     },
50057     /**
50058      * @property {Object} the last set data for the element
50059      */
50060     
50061     lastData : false,
50062     /**
50063      * Sets the value of the field based on a object which is related to the record format for the store.
50064      * @param {Object} value the value to set as. or false on reset?
50065      */
50066     setFromData : function(o){
50067         Roo.log('setfrom data?');
50068          
50069         
50070         
50071     },
50072     // private
50073     reset : function(){
50074         this.clearValue();
50075     },
50076     // private
50077     findRecord : function(prop, value){
50078         
50079         return false;
50080     
50081         var record;
50082         if(this.store.getCount() > 0){
50083             this.store.each(function(r){
50084                 if(r.data[prop] == value){
50085                     record = r;
50086                     return false;
50087                 }
50088                 return true;
50089             });
50090         }
50091         return record;
50092     },
50093     
50094     getName: function()
50095     {
50096         // returns hidden if it's set..
50097         if (!this.rendered) {return ''};
50098         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50099         
50100     },
50101      
50102
50103     
50104
50105     // private
50106     onEmptyResults : function(){
50107         Roo.log('empty results');
50108         //this.collapse();
50109     },
50110
50111     /**
50112      * Returns true if the dropdown list is expanded, else false.
50113      */
50114     isExpanded : function(){
50115         return false;
50116     },
50117
50118     /**
50119      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50120      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50121      * @param {String} value The data value of the item to select
50122      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50123      * selected item if it is not currently in view (defaults to true)
50124      * @return {Boolean} True if the value matched an item in the list, else false
50125      */
50126     selectByValue : function(v, scrollIntoView){
50127         Roo.log('select By Value');
50128         return false;
50129     
50130         if(v !== undefined && v !== null){
50131             var r = this.findRecord(this.valueField || this.displayField, v);
50132             if(r){
50133                 this.select(this.store.indexOf(r), scrollIntoView);
50134                 return true;
50135             }
50136         }
50137         return false;
50138     },
50139
50140     /**
50141      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50142      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50143      * @param {Number} index The zero-based index of the list item to select
50144      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50145      * selected item if it is not currently in view (defaults to true)
50146      */
50147     select : function(index, scrollIntoView){
50148         Roo.log('select ');
50149         return  ;
50150         
50151         this.selectedIndex = index;
50152         this.view.select(index);
50153         if(scrollIntoView !== false){
50154             var el = this.view.getNode(index);
50155             if(el){
50156                 this.innerList.scrollChildIntoView(el, false);
50157             }
50158         }
50159     },
50160
50161       
50162
50163     // private
50164     validateBlur : function(){
50165         
50166         return;
50167         
50168     },
50169
50170     // private
50171     initQuery : function(){
50172         this.doQuery(this.getRawValue());
50173     },
50174
50175     // private
50176     doForce : function(){
50177         if(this.el.dom.value.length > 0){
50178             this.el.dom.value =
50179                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50180              
50181         }
50182     },
50183
50184     /**
50185      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50186      * query allowing the query action to be canceled if needed.
50187      * @param {String} query The SQL query to execute
50188      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50189      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50190      * saved in the current store (defaults to false)
50191      */
50192     doQuery : function(q, forceAll){
50193         
50194         Roo.log('doQuery?');
50195         if(q === undefined || q === null){
50196             q = '';
50197         }
50198         var qe = {
50199             query: q,
50200             forceAll: forceAll,
50201             combo: this,
50202             cancel:false
50203         };
50204         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50205             return false;
50206         }
50207         q = qe.query;
50208         forceAll = qe.forceAll;
50209         if(forceAll === true || (q.length >= this.minChars)){
50210             if(this.lastQuery != q || this.alwaysQuery){
50211                 this.lastQuery = q;
50212                 if(this.mode == 'local'){
50213                     this.selectedIndex = -1;
50214                     if(forceAll){
50215                         this.store.clearFilter();
50216                     }else{
50217                         this.store.filter(this.displayField, q);
50218                     }
50219                     this.onLoad();
50220                 }else{
50221                     this.store.baseParams[this.queryParam] = q;
50222                     this.store.load({
50223                         params: this.getParams(q)
50224                     });
50225                     this.expand();
50226                 }
50227             }else{
50228                 this.selectedIndex = -1;
50229                 this.onLoad();   
50230             }
50231         }
50232     },
50233
50234     // private
50235     getParams : function(q){
50236         var p = {};
50237         //p[this.queryParam] = q;
50238         if(this.pageSize){
50239             p.start = 0;
50240             p.limit = this.pageSize;
50241         }
50242         return p;
50243     },
50244
50245     /**
50246      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50247      */
50248     collapse : function(){
50249         
50250     },
50251
50252     // private
50253     collapseIf : function(e){
50254         
50255     },
50256
50257     /**
50258      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50259      */
50260     expand : function(){
50261         
50262     } ,
50263
50264     // private
50265      
50266
50267     /** 
50268     * @cfg {Boolean} grow 
50269     * @hide 
50270     */
50271     /** 
50272     * @cfg {Number} growMin 
50273     * @hide 
50274     */
50275     /** 
50276     * @cfg {Number} growMax 
50277     * @hide 
50278     */
50279     /**
50280      * @hide
50281      * @method autoSize
50282      */
50283     
50284     setWidth : function()
50285     {
50286         
50287     },
50288     getResizeEl : function(){
50289         return this.el;
50290     }
50291 });//<script type="text/javasscript">
50292  
50293
50294 /**
50295  * @class Roo.DDView
50296  * A DnD enabled version of Roo.View.
50297  * @param {Element/String} container The Element in which to create the View.
50298  * @param {String} tpl The template string used to create the markup for each element of the View
50299  * @param {Object} config The configuration properties. These include all the config options of
50300  * {@link Roo.View} plus some specific to this class.<br>
50301  * <p>
50302  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50303  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50304  * <p>
50305  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50306 .x-view-drag-insert-above {
50307         border-top:1px dotted #3366cc;
50308 }
50309 .x-view-drag-insert-below {
50310         border-bottom:1px dotted #3366cc;
50311 }
50312 </code></pre>
50313  * 
50314  */
50315  
50316 Roo.DDView = function(container, tpl, config) {
50317     Roo.DDView.superclass.constructor.apply(this, arguments);
50318     this.getEl().setStyle("outline", "0px none");
50319     this.getEl().unselectable();
50320     if (this.dragGroup) {
50321                 this.setDraggable(this.dragGroup.split(","));
50322     }
50323     if (this.dropGroup) {
50324                 this.setDroppable(this.dropGroup.split(","));
50325     }
50326     if (this.deletable) {
50327         this.setDeletable();
50328     }
50329     this.isDirtyFlag = false;
50330         this.addEvents({
50331                 "drop" : true
50332         });
50333 };
50334
50335 Roo.extend(Roo.DDView, Roo.View, {
50336 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50337 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50338 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50339 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50340
50341         isFormField: true,
50342
50343         reset: Roo.emptyFn,
50344         
50345         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50346
50347         validate: function() {
50348                 return true;
50349         },
50350         
50351         destroy: function() {
50352                 this.purgeListeners();
50353                 this.getEl.removeAllListeners();
50354                 this.getEl().remove();
50355                 if (this.dragZone) {
50356                         if (this.dragZone.destroy) {
50357                                 this.dragZone.destroy();
50358                         }
50359                 }
50360                 if (this.dropZone) {
50361                         if (this.dropZone.destroy) {
50362                                 this.dropZone.destroy();
50363                         }
50364                 }
50365         },
50366
50367 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50368         getName: function() {
50369                 return this.name;
50370         },
50371
50372 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50373         setValue: function(v) {
50374                 if (!this.store) {
50375                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50376                 }
50377                 var data = {};
50378                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50379                 this.store.proxy = new Roo.data.MemoryProxy(data);
50380                 this.store.load();
50381         },
50382
50383 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50384         getValue: function() {
50385                 var result = '(';
50386                 this.store.each(function(rec) {
50387                         result += rec.id + ',';
50388                 });
50389                 return result.substr(0, result.length - 1) + ')';
50390         },
50391         
50392         getIds: function() {
50393                 var i = 0, result = new Array(this.store.getCount());
50394                 this.store.each(function(rec) {
50395                         result[i++] = rec.id;
50396                 });
50397                 return result;
50398         },
50399         
50400         isDirty: function() {
50401                 return this.isDirtyFlag;
50402         },
50403
50404 /**
50405  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50406  *      whole Element becomes the target, and this causes the drop gesture to append.
50407  */
50408     getTargetFromEvent : function(e) {
50409                 var target = e.getTarget();
50410                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50411                 target = target.parentNode;
50412                 }
50413                 if (!target) {
50414                         target = this.el.dom.lastChild || this.el.dom;
50415                 }
50416                 return target;
50417     },
50418
50419 /**
50420  *      Create the drag data which consists of an object which has the property "ddel" as
50421  *      the drag proxy element. 
50422  */
50423     getDragData : function(e) {
50424         var target = this.findItemFromChild(e.getTarget());
50425                 if(target) {
50426                         this.handleSelection(e);
50427                         var selNodes = this.getSelectedNodes();
50428             var dragData = {
50429                 source: this,
50430                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50431                 nodes: selNodes,
50432                 records: []
50433                         };
50434                         var selectedIndices = this.getSelectedIndexes();
50435                         for (var i = 0; i < selectedIndices.length; i++) {
50436                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50437                         }
50438                         if (selNodes.length == 1) {
50439                                 dragData.ddel = target.cloneNode(true); // the div element
50440                         } else {
50441                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50442                                 div.className = 'multi-proxy';
50443                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50444                                         div.appendChild(selNodes[i].cloneNode(true));
50445                                 }
50446                                 dragData.ddel = div;
50447                         }
50448             //console.log(dragData)
50449             //console.log(dragData.ddel.innerHTML)
50450                         return dragData;
50451                 }
50452         //console.log('nodragData')
50453                 return false;
50454     },
50455     
50456 /**     Specify to which ddGroup items in this DDView may be dragged. */
50457     setDraggable: function(ddGroup) {
50458         if (ddGroup instanceof Array) {
50459                 Roo.each(ddGroup, this.setDraggable, this);
50460                 return;
50461         }
50462         if (this.dragZone) {
50463                 this.dragZone.addToGroup(ddGroup);
50464         } else {
50465                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50466                                 containerScroll: true,
50467                                 ddGroup: ddGroup 
50468
50469                         });
50470 //                      Draggability implies selection. DragZone's mousedown selects the element.
50471                         if (!this.multiSelect) { this.singleSelect = true; }
50472
50473 //                      Wire the DragZone's handlers up to methods in *this*
50474                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50475                 }
50476     },
50477
50478 /**     Specify from which ddGroup this DDView accepts drops. */
50479     setDroppable: function(ddGroup) {
50480         if (ddGroup instanceof Array) {
50481                 Roo.each(ddGroup, this.setDroppable, this);
50482                 return;
50483         }
50484         if (this.dropZone) {
50485                 this.dropZone.addToGroup(ddGroup);
50486         } else {
50487                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50488                                 containerScroll: true,
50489                                 ddGroup: ddGroup
50490                         });
50491
50492 //                      Wire the DropZone's handlers up to methods in *this*
50493                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50494                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50495                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50496                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50497                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50498                 }
50499     },
50500
50501 /**     Decide whether to drop above or below a View node. */
50502     getDropPoint : function(e, n, dd){
50503         if (n == this.el.dom) { return "above"; }
50504                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50505                 var c = t + (b - t) / 2;
50506                 var y = Roo.lib.Event.getPageY(e);
50507                 if(y <= c) {
50508                         return "above";
50509                 }else{
50510                         return "below";
50511                 }
50512     },
50513
50514     onNodeEnter : function(n, dd, e, data){
50515                 return false;
50516     },
50517     
50518     onNodeOver : function(n, dd, e, data){
50519                 var pt = this.getDropPoint(e, n, dd);
50520                 // set the insert point style on the target node
50521                 var dragElClass = this.dropNotAllowed;
50522                 if (pt) {
50523                         var targetElClass;
50524                         if (pt == "above"){
50525                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50526                                 targetElClass = "x-view-drag-insert-above";
50527                         } else {
50528                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50529                                 targetElClass = "x-view-drag-insert-below";
50530                         }
50531                         if (this.lastInsertClass != targetElClass){
50532                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50533                                 this.lastInsertClass = targetElClass;
50534                         }
50535                 }
50536                 return dragElClass;
50537         },
50538
50539     onNodeOut : function(n, dd, e, data){
50540                 this.removeDropIndicators(n);
50541     },
50542
50543     onNodeDrop : function(n, dd, e, data){
50544         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50545                 return false;
50546         }
50547         var pt = this.getDropPoint(e, n, dd);
50548                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50549                 if (pt == "below") { insertAt++; }
50550                 for (var i = 0; i < data.records.length; i++) {
50551                         var r = data.records[i];
50552                         var dup = this.store.getById(r.id);
50553                         if (dup && (dd != this.dragZone)) {
50554                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50555                         } else {
50556                                 if (data.copy) {
50557                                         this.store.insert(insertAt++, r.copy());
50558                                 } else {
50559                                         data.source.isDirtyFlag = true;
50560                                         r.store.remove(r);
50561                                         this.store.insert(insertAt++, r);
50562                                 }
50563                                 this.isDirtyFlag = true;
50564                         }
50565                 }
50566                 this.dragZone.cachedTarget = null;
50567                 return true;
50568     },
50569
50570     removeDropIndicators : function(n){
50571                 if(n){
50572                         Roo.fly(n).removeClass([
50573                                 "x-view-drag-insert-above",
50574                                 "x-view-drag-insert-below"]);
50575                         this.lastInsertClass = "_noclass";
50576                 }
50577     },
50578
50579 /**
50580  *      Utility method. Add a delete option to the DDView's context menu.
50581  *      @param {String} imageUrl The URL of the "delete" icon image.
50582  */
50583         setDeletable: function(imageUrl) {
50584                 if (!this.singleSelect && !this.multiSelect) {
50585                         this.singleSelect = true;
50586                 }
50587                 var c = this.getContextMenu();
50588                 this.contextMenu.on("itemclick", function(item) {
50589                         switch (item.id) {
50590                                 case "delete":
50591                                         this.remove(this.getSelectedIndexes());
50592                                         break;
50593                         }
50594                 }, this);
50595                 this.contextMenu.add({
50596                         icon: imageUrl,
50597                         id: "delete",
50598                         text: 'Delete'
50599                 });
50600         },
50601         
50602 /**     Return the context menu for this DDView. */
50603         getContextMenu: function() {
50604                 if (!this.contextMenu) {
50605 //                      Create the View's context menu
50606                         this.contextMenu = new Roo.menu.Menu({
50607                                 id: this.id + "-contextmenu"
50608                         });
50609                         this.el.on("contextmenu", this.showContextMenu, this);
50610                 }
50611                 return this.contextMenu;
50612         },
50613         
50614         disableContextMenu: function() {
50615                 if (this.contextMenu) {
50616                         this.el.un("contextmenu", this.showContextMenu, this);
50617                 }
50618         },
50619
50620         showContextMenu: function(e, item) {
50621         item = this.findItemFromChild(e.getTarget());
50622                 if (item) {
50623                         e.stopEvent();
50624                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50625                         this.contextMenu.showAt(e.getXY());
50626             }
50627     },
50628
50629 /**
50630  *      Remove {@link Roo.data.Record}s at the specified indices.
50631  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50632  */
50633     remove: function(selectedIndices) {
50634                 selectedIndices = [].concat(selectedIndices);
50635                 for (var i = 0; i < selectedIndices.length; i++) {
50636                         var rec = this.store.getAt(selectedIndices[i]);
50637                         this.store.remove(rec);
50638                 }
50639     },
50640
50641 /**
50642  *      Double click fires the event, but also, if this is draggable, and there is only one other
50643  *      related DropZone, it transfers the selected node.
50644  */
50645     onDblClick : function(e){
50646         var item = this.findItemFromChild(e.getTarget());
50647         if(item){
50648             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50649                 return false;
50650             }
50651             if (this.dragGroup) {
50652                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50653                     while (targets.indexOf(this.dropZone) > -1) {
50654                             targets.remove(this.dropZone);
50655                                 }
50656                     if (targets.length == 1) {
50657                                         this.dragZone.cachedTarget = null;
50658                         var el = Roo.get(targets[0].getEl());
50659                         var box = el.getBox(true);
50660                         targets[0].onNodeDrop(el.dom, {
50661                                 target: el.dom,
50662                                 xy: [box.x, box.y + box.height - 1]
50663                         }, null, this.getDragData(e));
50664                     }
50665                 }
50666         }
50667     },
50668     
50669     handleSelection: function(e) {
50670                 this.dragZone.cachedTarget = null;
50671         var item = this.findItemFromChild(e.getTarget());
50672         if (!item) {
50673                 this.clearSelections(true);
50674                 return;
50675         }
50676                 if (item && (this.multiSelect || this.singleSelect)){
50677                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50678                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50679                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50680                                 this.unselect(item);
50681                         } else {
50682                                 this.select(item, this.multiSelect && e.ctrlKey);
50683                                 this.lastSelection = item;
50684                         }
50685                 }
50686     },
50687
50688     onItemClick : function(item, index, e){
50689                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50690                         return false;
50691                 }
50692                 return true;
50693     },
50694
50695     unselect : function(nodeInfo, suppressEvent){
50696                 var node = this.getNode(nodeInfo);
50697                 if(node && this.isSelected(node)){
50698                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50699                                 Roo.fly(node).removeClass(this.selectedClass);
50700                                 this.selections.remove(node);
50701                                 if(!suppressEvent){
50702                                         this.fireEvent("selectionchange", this, this.selections);
50703                                 }
50704                         }
50705                 }
50706     }
50707 });
50708 /*
50709  * Based on:
50710  * Ext JS Library 1.1.1
50711  * Copyright(c) 2006-2007, Ext JS, LLC.
50712  *
50713  * Originally Released Under LGPL - original licence link has changed is not relivant.
50714  *
50715  * Fork - LGPL
50716  * <script type="text/javascript">
50717  */
50718  
50719 /**
50720  * @class Roo.LayoutManager
50721  * @extends Roo.util.Observable
50722  * Base class for layout managers.
50723  */
50724 Roo.LayoutManager = function(container, config){
50725     Roo.LayoutManager.superclass.constructor.call(this);
50726     this.el = Roo.get(container);
50727     // ie scrollbar fix
50728     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50729         document.body.scroll = "no";
50730     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50731         this.el.position('relative');
50732     }
50733     this.id = this.el.id;
50734     this.el.addClass("x-layout-container");
50735     /** false to disable window resize monitoring @type Boolean */
50736     this.monitorWindowResize = true;
50737     this.regions = {};
50738     this.addEvents({
50739         /**
50740          * @event layout
50741          * Fires when a layout is performed. 
50742          * @param {Roo.LayoutManager} this
50743          */
50744         "layout" : true,
50745         /**
50746          * @event regionresized
50747          * Fires when the user resizes a region. 
50748          * @param {Roo.LayoutRegion} region The resized region
50749          * @param {Number} newSize The new size (width for east/west, height for north/south)
50750          */
50751         "regionresized" : true,
50752         /**
50753          * @event regioncollapsed
50754          * Fires when a region is collapsed. 
50755          * @param {Roo.LayoutRegion} region The collapsed region
50756          */
50757         "regioncollapsed" : true,
50758         /**
50759          * @event regionexpanded
50760          * Fires when a region is expanded.  
50761          * @param {Roo.LayoutRegion} region The expanded region
50762          */
50763         "regionexpanded" : true
50764     });
50765     this.updating = false;
50766     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50767 };
50768
50769 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50770     /**
50771      * Returns true if this layout is currently being updated
50772      * @return {Boolean}
50773      */
50774     isUpdating : function(){
50775         return this.updating; 
50776     },
50777     
50778     /**
50779      * Suspend the LayoutManager from doing auto-layouts while
50780      * making multiple add or remove calls
50781      */
50782     beginUpdate : function(){
50783         this.updating = true;    
50784     },
50785     
50786     /**
50787      * Restore auto-layouts and optionally disable the manager from performing a layout
50788      * @param {Boolean} noLayout true to disable a layout update 
50789      */
50790     endUpdate : function(noLayout){
50791         this.updating = false;
50792         if(!noLayout){
50793             this.layout();
50794         }    
50795     },
50796     
50797     layout: function(){
50798         
50799     },
50800     
50801     onRegionResized : function(region, newSize){
50802         this.fireEvent("regionresized", region, newSize);
50803         this.layout();
50804     },
50805     
50806     onRegionCollapsed : function(region){
50807         this.fireEvent("regioncollapsed", region);
50808     },
50809     
50810     onRegionExpanded : function(region){
50811         this.fireEvent("regionexpanded", region);
50812     },
50813         
50814     /**
50815      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50816      * performs box-model adjustments.
50817      * @return {Object} The size as an object {width: (the width), height: (the height)}
50818      */
50819     getViewSize : function(){
50820         var size;
50821         if(this.el.dom != document.body){
50822             size = this.el.getSize();
50823         }else{
50824             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50825         }
50826         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50827         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50828         return size;
50829     },
50830     
50831     /**
50832      * Returns the Element this layout is bound to.
50833      * @return {Roo.Element}
50834      */
50835     getEl : function(){
50836         return this.el;
50837     },
50838     
50839     /**
50840      * Returns the specified region.
50841      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50842      * @return {Roo.LayoutRegion}
50843      */
50844     getRegion : function(target){
50845         return this.regions[target.toLowerCase()];
50846     },
50847     
50848     onWindowResize : function(){
50849         if(this.monitorWindowResize){
50850             this.layout();
50851         }
50852     }
50853 });/*
50854  * Based on:
50855  * Ext JS Library 1.1.1
50856  * Copyright(c) 2006-2007, Ext JS, LLC.
50857  *
50858  * Originally Released Under LGPL - original licence link has changed is not relivant.
50859  *
50860  * Fork - LGPL
50861  * <script type="text/javascript">
50862  */
50863 /**
50864  * @class Roo.BorderLayout
50865  * @extends Roo.LayoutManager
50866  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50867  * please see: <br><br>
50868  * <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>
50869  * <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>
50870  * Example:
50871  <pre><code>
50872  var layout = new Roo.BorderLayout(document.body, {
50873     north: {
50874         initialSize: 25,
50875         titlebar: false
50876     },
50877     west: {
50878         split:true,
50879         initialSize: 200,
50880         minSize: 175,
50881         maxSize: 400,
50882         titlebar: true,
50883         collapsible: true
50884     },
50885     east: {
50886         split:true,
50887         initialSize: 202,
50888         minSize: 175,
50889         maxSize: 400,
50890         titlebar: true,
50891         collapsible: true
50892     },
50893     south: {
50894         split:true,
50895         initialSize: 100,
50896         minSize: 100,
50897         maxSize: 200,
50898         titlebar: true,
50899         collapsible: true
50900     },
50901     center: {
50902         titlebar: true,
50903         autoScroll:true,
50904         resizeTabs: true,
50905         minTabWidth: 50,
50906         preferredTabWidth: 150
50907     }
50908 });
50909
50910 // shorthand
50911 var CP = Roo.ContentPanel;
50912
50913 layout.beginUpdate();
50914 layout.add("north", new CP("north", "North"));
50915 layout.add("south", new CP("south", {title: "South", closable: true}));
50916 layout.add("west", new CP("west", {title: "West"}));
50917 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50918 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50919 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50920 layout.getRegion("center").showPanel("center1");
50921 layout.endUpdate();
50922 </code></pre>
50923
50924 <b>The container the layout is rendered into can be either the body element or any other element.
50925 If it is not the body element, the container needs to either be an absolute positioned element,
50926 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50927 the container size if it is not the body element.</b>
50928
50929 * @constructor
50930 * Create a new BorderLayout
50931 * @param {String/HTMLElement/Element} container The container this layout is bound to
50932 * @param {Object} config Configuration options
50933  */
50934 Roo.BorderLayout = function(container, config){
50935     config = config || {};
50936     Roo.BorderLayout.superclass.constructor.call(this, container, config);
50937     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
50938     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
50939         var target = this.factory.validRegions[i];
50940         if(config[target]){
50941             this.addRegion(target, config[target]);
50942         }
50943     }
50944 };
50945
50946 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
50947     /**
50948      * Creates and adds a new region if it doesn't already exist.
50949      * @param {String} target The target region key (north, south, east, west or center).
50950      * @param {Object} config The regions config object
50951      * @return {BorderLayoutRegion} The new region
50952      */
50953     addRegion : function(target, config){
50954         if(!this.regions[target]){
50955             var r = this.factory.create(target, this, config);
50956             this.bindRegion(target, r);
50957         }
50958         return this.regions[target];
50959     },
50960
50961     // private (kinda)
50962     bindRegion : function(name, r){
50963         this.regions[name] = r;
50964         r.on("visibilitychange", this.layout, this);
50965         r.on("paneladded", this.layout, this);
50966         r.on("panelremoved", this.layout, this);
50967         r.on("invalidated", this.layout, this);
50968         r.on("resized", this.onRegionResized, this);
50969         r.on("collapsed", this.onRegionCollapsed, this);
50970         r.on("expanded", this.onRegionExpanded, this);
50971     },
50972
50973     /**
50974      * Performs a layout update.
50975      */
50976     layout : function(){
50977         if(this.updating) {
50978             return;
50979         }
50980         var size = this.getViewSize();
50981         var w = size.width;
50982         var h = size.height;
50983         var centerW = w;
50984         var centerH = h;
50985         var centerY = 0;
50986         var centerX = 0;
50987         //var x = 0, y = 0;
50988
50989         var rs = this.regions;
50990         var north = rs["north"];
50991         var south = rs["south"]; 
50992         var west = rs["west"];
50993         var east = rs["east"];
50994         var center = rs["center"];
50995         //if(this.hideOnLayout){ // not supported anymore
50996             //c.el.setStyle("display", "none");
50997         //}
50998         if(north && north.isVisible()){
50999             var b = north.getBox();
51000             var m = north.getMargins();
51001             b.width = w - (m.left+m.right);
51002             b.x = m.left;
51003             b.y = m.top;
51004             centerY = b.height + b.y + m.bottom;
51005             centerH -= centerY;
51006             north.updateBox(this.safeBox(b));
51007         }
51008         if(south && south.isVisible()){
51009             var b = south.getBox();
51010             var m = south.getMargins();
51011             b.width = w - (m.left+m.right);
51012             b.x = m.left;
51013             var totalHeight = (b.height + m.top + m.bottom);
51014             b.y = h - totalHeight + m.top;
51015             centerH -= totalHeight;
51016             south.updateBox(this.safeBox(b));
51017         }
51018         if(west && west.isVisible()){
51019             var b = west.getBox();
51020             var m = west.getMargins();
51021             b.height = centerH - (m.top+m.bottom);
51022             b.x = m.left;
51023             b.y = centerY + m.top;
51024             var totalWidth = (b.width + m.left + m.right);
51025             centerX += totalWidth;
51026             centerW -= totalWidth;
51027             west.updateBox(this.safeBox(b));
51028         }
51029         if(east && east.isVisible()){
51030             var b = east.getBox();
51031             var m = east.getMargins();
51032             b.height = centerH - (m.top+m.bottom);
51033             var totalWidth = (b.width + m.left + m.right);
51034             b.x = w - totalWidth + m.left;
51035             b.y = centerY + m.top;
51036             centerW -= totalWidth;
51037             east.updateBox(this.safeBox(b));
51038         }
51039         if(center){
51040             var m = center.getMargins();
51041             var centerBox = {
51042                 x: centerX + m.left,
51043                 y: centerY + m.top,
51044                 width: centerW - (m.left+m.right),
51045                 height: centerH - (m.top+m.bottom)
51046             };
51047             //if(this.hideOnLayout){
51048                 //center.el.setStyle("display", "block");
51049             //}
51050             center.updateBox(this.safeBox(centerBox));
51051         }
51052         this.el.repaint();
51053         this.fireEvent("layout", this);
51054     },
51055
51056     // private
51057     safeBox : function(box){
51058         box.width = Math.max(0, box.width);
51059         box.height = Math.max(0, box.height);
51060         return box;
51061     },
51062
51063     /**
51064      * Adds a ContentPanel (or subclass) to this layout.
51065      * @param {String} target The target region key (north, south, east, west or center).
51066      * @param {Roo.ContentPanel} panel The panel to add
51067      * @return {Roo.ContentPanel} The added panel
51068      */
51069     add : function(target, panel){
51070          
51071         target = target.toLowerCase();
51072         return this.regions[target].add(panel);
51073     },
51074
51075     /**
51076      * Remove a ContentPanel (or subclass) to this layout.
51077      * @param {String} target The target region key (north, south, east, west or center).
51078      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51079      * @return {Roo.ContentPanel} The removed panel
51080      */
51081     remove : function(target, panel){
51082         target = target.toLowerCase();
51083         return this.regions[target].remove(panel);
51084     },
51085
51086     /**
51087      * Searches all regions for a panel with the specified id
51088      * @param {String} panelId
51089      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51090      */
51091     findPanel : function(panelId){
51092         var rs = this.regions;
51093         for(var target in rs){
51094             if(typeof rs[target] != "function"){
51095                 var p = rs[target].getPanel(panelId);
51096                 if(p){
51097                     return p;
51098                 }
51099             }
51100         }
51101         return null;
51102     },
51103
51104     /**
51105      * Searches all regions for a panel with the specified id and activates (shows) it.
51106      * @param {String/ContentPanel} panelId The panels id or the panel itself
51107      * @return {Roo.ContentPanel} The shown panel or null
51108      */
51109     showPanel : function(panelId) {
51110       var rs = this.regions;
51111       for(var target in rs){
51112          var r = rs[target];
51113          if(typeof r != "function"){
51114             if(r.hasPanel(panelId)){
51115                return r.showPanel(panelId);
51116             }
51117          }
51118       }
51119       return null;
51120    },
51121
51122    /**
51123      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51124      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51125      */
51126     restoreState : function(provider){
51127         if(!provider){
51128             provider = Roo.state.Manager;
51129         }
51130         var sm = new Roo.LayoutStateManager();
51131         sm.init(this, provider);
51132     },
51133
51134     /**
51135      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51136      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51137      * a valid ContentPanel config object.  Example:
51138      * <pre><code>
51139 // Create the main layout
51140 var layout = new Roo.BorderLayout('main-ct', {
51141     west: {
51142         split:true,
51143         minSize: 175,
51144         titlebar: true
51145     },
51146     center: {
51147         title:'Components'
51148     }
51149 }, 'main-ct');
51150
51151 // Create and add multiple ContentPanels at once via configs
51152 layout.batchAdd({
51153    west: {
51154        id: 'source-files',
51155        autoCreate:true,
51156        title:'Ext Source Files',
51157        autoScroll:true,
51158        fitToFrame:true
51159    },
51160    center : {
51161        el: cview,
51162        autoScroll:true,
51163        fitToFrame:true,
51164        toolbar: tb,
51165        resizeEl:'cbody'
51166    }
51167 });
51168 </code></pre>
51169      * @param {Object} regions An object containing ContentPanel configs by region name
51170      */
51171     batchAdd : function(regions){
51172         this.beginUpdate();
51173         for(var rname in regions){
51174             var lr = this.regions[rname];
51175             if(lr){
51176                 this.addTypedPanels(lr, regions[rname]);
51177             }
51178         }
51179         this.endUpdate();
51180     },
51181
51182     // private
51183     addTypedPanels : function(lr, ps){
51184         if(typeof ps == 'string'){
51185             lr.add(new Roo.ContentPanel(ps));
51186         }
51187         else if(ps instanceof Array){
51188             for(var i =0, len = ps.length; i < len; i++){
51189                 this.addTypedPanels(lr, ps[i]);
51190             }
51191         }
51192         else if(!ps.events){ // raw config?
51193             var el = ps.el;
51194             delete ps.el; // prevent conflict
51195             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51196         }
51197         else {  // panel object assumed!
51198             lr.add(ps);
51199         }
51200     },
51201     /**
51202      * Adds a xtype elements to the layout.
51203      * <pre><code>
51204
51205 layout.addxtype({
51206        xtype : 'ContentPanel',
51207        region: 'west',
51208        items: [ .... ]
51209    }
51210 );
51211
51212 layout.addxtype({
51213         xtype : 'NestedLayoutPanel',
51214         region: 'west',
51215         layout: {
51216            center: { },
51217            west: { }   
51218         },
51219         items : [ ... list of content panels or nested layout panels.. ]
51220    }
51221 );
51222 </code></pre>
51223      * @param {Object} cfg Xtype definition of item to add.
51224      */
51225     addxtype : function(cfg)
51226     {
51227         // basically accepts a pannel...
51228         // can accept a layout region..!?!?
51229         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51230         
51231         if (!cfg.xtype.match(/Panel$/)) {
51232             return false;
51233         }
51234         var ret = false;
51235         
51236         if (typeof(cfg.region) == 'undefined') {
51237             Roo.log("Failed to add Panel, region was not set");
51238             Roo.log(cfg);
51239             return false;
51240         }
51241         var region = cfg.region;
51242         delete cfg.region;
51243         
51244           
51245         var xitems = [];
51246         if (cfg.items) {
51247             xitems = cfg.items;
51248             delete cfg.items;
51249         }
51250         var nb = false;
51251         
51252         switch(cfg.xtype) 
51253         {
51254             case 'ContentPanel':  // ContentPanel (el, cfg)
51255             case 'ScrollPanel':  // ContentPanel (el, cfg)
51256             case 'ViewPanel': 
51257                 if(cfg.autoCreate) {
51258                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51259                 } else {
51260                     var el = this.el.createChild();
51261                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51262                 }
51263                 
51264                 this.add(region, ret);
51265                 break;
51266             
51267             
51268             case 'TreePanel': // our new panel!
51269                 cfg.el = this.el.createChild();
51270                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51271                 this.add(region, ret);
51272                 break;
51273             
51274             case 'NestedLayoutPanel': 
51275                 // create a new Layout (which is  a Border Layout...
51276                 var el = this.el.createChild();
51277                 var clayout = cfg.layout;
51278                 delete cfg.layout;
51279                 clayout.items   = clayout.items  || [];
51280                 // replace this exitems with the clayout ones..
51281                 xitems = clayout.items;
51282                  
51283                 
51284                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51285                     cfg.background = false;
51286                 }
51287                 var layout = new Roo.BorderLayout(el, clayout);
51288                 
51289                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51290                 //console.log('adding nested layout panel '  + cfg.toSource());
51291                 this.add(region, ret);
51292                 nb = {}; /// find first...
51293                 break;
51294                 
51295             case 'GridPanel': 
51296             
51297                 // needs grid and region
51298                 
51299                 //var el = this.getRegion(region).el.createChild();
51300                 var el = this.el.createChild();
51301                 // create the grid first...
51302                 
51303                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51304                 delete cfg.grid;
51305                 if (region == 'center' && this.active ) {
51306                     cfg.background = false;
51307                 }
51308                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51309                 
51310                 this.add(region, ret);
51311                 if (cfg.background) {
51312                     ret.on('activate', function(gp) {
51313                         if (!gp.grid.rendered) {
51314                             gp.grid.render();
51315                         }
51316                     });
51317                 } else {
51318                     grid.render();
51319                 }
51320                 break;
51321            
51322            
51323            
51324                 
51325                 
51326                 
51327             default:
51328                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51329                     
51330                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51331                     this.add(region, ret);
51332                 } else {
51333                 
51334                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51335                     return null;
51336                 }
51337                 
51338              // GridPanel (grid, cfg)
51339             
51340         }
51341         this.beginUpdate();
51342         // add children..
51343         var region = '';
51344         var abn = {};
51345         Roo.each(xitems, function(i)  {
51346             region = nb && i.region ? i.region : false;
51347             
51348             var add = ret.addxtype(i);
51349            
51350             if (region) {
51351                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51352                 if (!i.background) {
51353                     abn[region] = nb[region] ;
51354                 }
51355             }
51356             
51357         });
51358         this.endUpdate();
51359
51360         // make the last non-background panel active..
51361         //if (nb) { Roo.log(abn); }
51362         if (nb) {
51363             
51364             for(var r in abn) {
51365                 region = this.getRegion(r);
51366                 if (region) {
51367                     // tried using nb[r], but it does not work..
51368                      
51369                     region.showPanel(abn[r]);
51370                    
51371                 }
51372             }
51373         }
51374         return ret;
51375         
51376     }
51377 });
51378
51379 /**
51380  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51381  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51382  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51383  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51384  * <pre><code>
51385 // shorthand
51386 var CP = Roo.ContentPanel;
51387
51388 var layout = Roo.BorderLayout.create({
51389     north: {
51390         initialSize: 25,
51391         titlebar: false,
51392         panels: [new CP("north", "North")]
51393     },
51394     west: {
51395         split:true,
51396         initialSize: 200,
51397         minSize: 175,
51398         maxSize: 400,
51399         titlebar: true,
51400         collapsible: true,
51401         panels: [new CP("west", {title: "West"})]
51402     },
51403     east: {
51404         split:true,
51405         initialSize: 202,
51406         minSize: 175,
51407         maxSize: 400,
51408         titlebar: true,
51409         collapsible: true,
51410         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51411     },
51412     south: {
51413         split:true,
51414         initialSize: 100,
51415         minSize: 100,
51416         maxSize: 200,
51417         titlebar: true,
51418         collapsible: true,
51419         panels: [new CP("south", {title: "South", closable: true})]
51420     },
51421     center: {
51422         titlebar: true,
51423         autoScroll:true,
51424         resizeTabs: true,
51425         minTabWidth: 50,
51426         preferredTabWidth: 150,
51427         panels: [
51428             new CP("center1", {title: "Close Me", closable: true}),
51429             new CP("center2", {title: "Center Panel", closable: false})
51430         ]
51431     }
51432 }, document.body);
51433
51434 layout.getRegion("center").showPanel("center1");
51435 </code></pre>
51436  * @param config
51437  * @param targetEl
51438  */
51439 Roo.BorderLayout.create = function(config, targetEl){
51440     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51441     layout.beginUpdate();
51442     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51443     for(var j = 0, jlen = regions.length; j < jlen; j++){
51444         var lr = regions[j];
51445         if(layout.regions[lr] && config[lr].panels){
51446             var r = layout.regions[lr];
51447             var ps = config[lr].panels;
51448             layout.addTypedPanels(r, ps);
51449         }
51450     }
51451     layout.endUpdate();
51452     return layout;
51453 };
51454
51455 // private
51456 Roo.BorderLayout.RegionFactory = {
51457     // private
51458     validRegions : ["north","south","east","west","center"],
51459
51460     // private
51461     create : function(target, mgr, config){
51462         target = target.toLowerCase();
51463         if(config.lightweight || config.basic){
51464             return new Roo.BasicLayoutRegion(mgr, config, target);
51465         }
51466         switch(target){
51467             case "north":
51468                 return new Roo.NorthLayoutRegion(mgr, config);
51469             case "south":
51470                 return new Roo.SouthLayoutRegion(mgr, config);
51471             case "east":
51472                 return new Roo.EastLayoutRegion(mgr, config);
51473             case "west":
51474                 return new Roo.WestLayoutRegion(mgr, config);
51475             case "center":
51476                 return new Roo.CenterLayoutRegion(mgr, config);
51477         }
51478         throw 'Layout region "'+target+'" not supported.';
51479     }
51480 };/*
51481  * Based on:
51482  * Ext JS Library 1.1.1
51483  * Copyright(c) 2006-2007, Ext JS, LLC.
51484  *
51485  * Originally Released Under LGPL - original licence link has changed is not relivant.
51486  *
51487  * Fork - LGPL
51488  * <script type="text/javascript">
51489  */
51490  
51491 /**
51492  * @class Roo.BasicLayoutRegion
51493  * @extends Roo.util.Observable
51494  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51495  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51496  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51497  */
51498 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51499     this.mgr = mgr;
51500     this.position  = pos;
51501     this.events = {
51502         /**
51503          * @scope Roo.BasicLayoutRegion
51504          */
51505         
51506         /**
51507          * @event beforeremove
51508          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51509          * @param {Roo.LayoutRegion} this
51510          * @param {Roo.ContentPanel} panel The panel
51511          * @param {Object} e The cancel event object
51512          */
51513         "beforeremove" : true,
51514         /**
51515          * @event invalidated
51516          * Fires when the layout for this region is changed.
51517          * @param {Roo.LayoutRegion} this
51518          */
51519         "invalidated" : true,
51520         /**
51521          * @event visibilitychange
51522          * Fires when this region is shown or hidden 
51523          * @param {Roo.LayoutRegion} this
51524          * @param {Boolean} visibility true or false
51525          */
51526         "visibilitychange" : true,
51527         /**
51528          * @event paneladded
51529          * Fires when a panel is added. 
51530          * @param {Roo.LayoutRegion} this
51531          * @param {Roo.ContentPanel} panel The panel
51532          */
51533         "paneladded" : true,
51534         /**
51535          * @event panelremoved
51536          * Fires when a panel is removed. 
51537          * @param {Roo.LayoutRegion} this
51538          * @param {Roo.ContentPanel} panel The panel
51539          */
51540         "panelremoved" : true,
51541         /**
51542          * @event beforecollapse
51543          * Fires when this region before collapse.
51544          * @param {Roo.LayoutRegion} this
51545          */
51546         "beforecollapse" : true,
51547         /**
51548          * @event collapsed
51549          * Fires when this region is collapsed.
51550          * @param {Roo.LayoutRegion} this
51551          */
51552         "collapsed" : true,
51553         /**
51554          * @event expanded
51555          * Fires when this region is expanded.
51556          * @param {Roo.LayoutRegion} this
51557          */
51558         "expanded" : true,
51559         /**
51560          * @event slideshow
51561          * Fires when this region is slid into view.
51562          * @param {Roo.LayoutRegion} this
51563          */
51564         "slideshow" : true,
51565         /**
51566          * @event slidehide
51567          * Fires when this region slides out of view. 
51568          * @param {Roo.LayoutRegion} this
51569          */
51570         "slidehide" : true,
51571         /**
51572          * @event panelactivated
51573          * Fires when a panel is activated. 
51574          * @param {Roo.LayoutRegion} this
51575          * @param {Roo.ContentPanel} panel The activated panel
51576          */
51577         "panelactivated" : true,
51578         /**
51579          * @event resized
51580          * Fires when the user resizes this region. 
51581          * @param {Roo.LayoutRegion} this
51582          * @param {Number} newSize The new size (width for east/west, height for north/south)
51583          */
51584         "resized" : true
51585     };
51586     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51587     this.panels = new Roo.util.MixedCollection();
51588     this.panels.getKey = this.getPanelId.createDelegate(this);
51589     this.box = null;
51590     this.activePanel = null;
51591     // ensure listeners are added...
51592     
51593     if (config.listeners || config.events) {
51594         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51595             listeners : config.listeners || {},
51596             events : config.events || {}
51597         });
51598     }
51599     
51600     if(skipConfig !== true){
51601         this.applyConfig(config);
51602     }
51603 };
51604
51605 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51606     getPanelId : function(p){
51607         return p.getId();
51608     },
51609     
51610     applyConfig : function(config){
51611         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51612         this.config = config;
51613         
51614     },
51615     
51616     /**
51617      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51618      * the width, for horizontal (north, south) the height.
51619      * @param {Number} newSize The new width or height
51620      */
51621     resizeTo : function(newSize){
51622         var el = this.el ? this.el :
51623                  (this.activePanel ? this.activePanel.getEl() : null);
51624         if(el){
51625             switch(this.position){
51626                 case "east":
51627                 case "west":
51628                     el.setWidth(newSize);
51629                     this.fireEvent("resized", this, newSize);
51630                 break;
51631                 case "north":
51632                 case "south":
51633                     el.setHeight(newSize);
51634                     this.fireEvent("resized", this, newSize);
51635                 break;                
51636             }
51637         }
51638     },
51639     
51640     getBox : function(){
51641         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51642     },
51643     
51644     getMargins : function(){
51645         return this.margins;
51646     },
51647     
51648     updateBox : function(box){
51649         this.box = box;
51650         var el = this.activePanel.getEl();
51651         el.dom.style.left = box.x + "px";
51652         el.dom.style.top = box.y + "px";
51653         this.activePanel.setSize(box.width, box.height);
51654     },
51655     
51656     /**
51657      * Returns the container element for this region.
51658      * @return {Roo.Element}
51659      */
51660     getEl : function(){
51661         return this.activePanel;
51662     },
51663     
51664     /**
51665      * Returns true if this region is currently visible.
51666      * @return {Boolean}
51667      */
51668     isVisible : function(){
51669         return this.activePanel ? true : false;
51670     },
51671     
51672     setActivePanel : function(panel){
51673         panel = this.getPanel(panel);
51674         if(this.activePanel && this.activePanel != panel){
51675             this.activePanel.setActiveState(false);
51676             this.activePanel.getEl().setLeftTop(-10000,-10000);
51677         }
51678         this.activePanel = panel;
51679         panel.setActiveState(true);
51680         if(this.box){
51681             panel.setSize(this.box.width, this.box.height);
51682         }
51683         this.fireEvent("panelactivated", this, panel);
51684         this.fireEvent("invalidated");
51685     },
51686     
51687     /**
51688      * Show the specified panel.
51689      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51690      * @return {Roo.ContentPanel} The shown panel or null
51691      */
51692     showPanel : function(panel){
51693         if(panel = this.getPanel(panel)){
51694             this.setActivePanel(panel);
51695         }
51696         return panel;
51697     },
51698     
51699     /**
51700      * Get the active panel for this region.
51701      * @return {Roo.ContentPanel} The active panel or null
51702      */
51703     getActivePanel : function(){
51704         return this.activePanel;
51705     },
51706     
51707     /**
51708      * Add the passed ContentPanel(s)
51709      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51710      * @return {Roo.ContentPanel} The panel added (if only one was added)
51711      */
51712     add : function(panel){
51713         if(arguments.length > 1){
51714             for(var i = 0, len = arguments.length; i < len; i++) {
51715                 this.add(arguments[i]);
51716             }
51717             return null;
51718         }
51719         if(this.hasPanel(panel)){
51720             this.showPanel(panel);
51721             return panel;
51722         }
51723         var el = panel.getEl();
51724         if(el.dom.parentNode != this.mgr.el.dom){
51725             this.mgr.el.dom.appendChild(el.dom);
51726         }
51727         if(panel.setRegion){
51728             panel.setRegion(this);
51729         }
51730         this.panels.add(panel);
51731         el.setStyle("position", "absolute");
51732         if(!panel.background){
51733             this.setActivePanel(panel);
51734             if(this.config.initialSize && this.panels.getCount()==1){
51735                 this.resizeTo(this.config.initialSize);
51736             }
51737         }
51738         this.fireEvent("paneladded", this, panel);
51739         return panel;
51740     },
51741     
51742     /**
51743      * Returns true if the panel is in this region.
51744      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51745      * @return {Boolean}
51746      */
51747     hasPanel : function(panel){
51748         if(typeof panel == "object"){ // must be panel obj
51749             panel = panel.getId();
51750         }
51751         return this.getPanel(panel) ? true : false;
51752     },
51753     
51754     /**
51755      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51756      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51757      * @param {Boolean} preservePanel Overrides the config preservePanel option
51758      * @return {Roo.ContentPanel} The panel that was removed
51759      */
51760     remove : function(panel, preservePanel){
51761         panel = this.getPanel(panel);
51762         if(!panel){
51763             return null;
51764         }
51765         var e = {};
51766         this.fireEvent("beforeremove", this, panel, e);
51767         if(e.cancel === true){
51768             return null;
51769         }
51770         var panelId = panel.getId();
51771         this.panels.removeKey(panelId);
51772         return panel;
51773     },
51774     
51775     /**
51776      * Returns the panel specified or null if it's not in this region.
51777      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51778      * @return {Roo.ContentPanel}
51779      */
51780     getPanel : function(id){
51781         if(typeof id == "object"){ // must be panel obj
51782             return id;
51783         }
51784         return this.panels.get(id);
51785     },
51786     
51787     /**
51788      * Returns this regions position (north/south/east/west/center).
51789      * @return {String} 
51790      */
51791     getPosition: function(){
51792         return this.position;    
51793     }
51794 });/*
51795  * Based on:
51796  * Ext JS Library 1.1.1
51797  * Copyright(c) 2006-2007, Ext JS, LLC.
51798  *
51799  * Originally Released Under LGPL - original licence link has changed is not relivant.
51800  *
51801  * Fork - LGPL
51802  * <script type="text/javascript">
51803  */
51804  
51805 /**
51806  * @class Roo.LayoutRegion
51807  * @extends Roo.BasicLayoutRegion
51808  * This class represents a region in a layout manager.
51809  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51810  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51811  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51812  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51813  * @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})
51814  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51815  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51816  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51817  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51818  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51819  * @cfg {String}    title           The title for the region (overrides panel titles)
51820  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51821  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51822  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51823  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51824  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51825  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51826  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51827  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51828  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51829  * @cfg {Boolean}   showPin         True to show a pin button
51830  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51831  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51832  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51833  * @cfg {Number}    width           For East/West panels
51834  * @cfg {Number}    height          For North/South panels
51835  * @cfg {Boolean}   split           To show the splitter
51836  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51837  */
51838 Roo.LayoutRegion = function(mgr, config, pos){
51839     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51840     var dh = Roo.DomHelper;
51841     /** This region's container element 
51842     * @type Roo.Element */
51843     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51844     /** This region's title element 
51845     * @type Roo.Element */
51846
51847     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51848         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51849         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51850     ]}, true);
51851     this.titleEl.enableDisplayMode();
51852     /** This region's title text element 
51853     * @type HTMLElement */
51854     this.titleTextEl = this.titleEl.dom.firstChild;
51855     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51856     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51857     this.closeBtn.enableDisplayMode();
51858     this.closeBtn.on("click", this.closeClicked, this);
51859     this.closeBtn.hide();
51860
51861     this.createBody(config);
51862     this.visible = true;
51863     this.collapsed = false;
51864
51865     if(config.hideWhenEmpty){
51866         this.hide();
51867         this.on("paneladded", this.validateVisibility, this);
51868         this.on("panelremoved", this.validateVisibility, this);
51869     }
51870     this.applyConfig(config);
51871 };
51872
51873 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51874
51875     createBody : function(){
51876         /** This region's body element 
51877         * @type Roo.Element */
51878         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51879     },
51880
51881     applyConfig : function(c){
51882         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51883             var dh = Roo.DomHelper;
51884             if(c.titlebar !== false){
51885                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51886                 this.collapseBtn.on("click", this.collapse, this);
51887                 this.collapseBtn.enableDisplayMode();
51888
51889                 if(c.showPin === true || this.showPin){
51890                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51891                     this.stickBtn.enableDisplayMode();
51892                     this.stickBtn.on("click", this.expand, this);
51893                     this.stickBtn.hide();
51894                 }
51895             }
51896             /** This region's collapsed element
51897             * @type Roo.Element */
51898             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51899                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51900             ]}, true);
51901             if(c.floatable !== false){
51902                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51903                this.collapsedEl.on("click", this.collapseClick, this);
51904             }
51905
51906             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51907                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51908                    id: "message", unselectable: "on", style:{"float":"left"}});
51909                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51910              }
51911             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51912             this.expandBtn.on("click", this.expand, this);
51913         }
51914         if(this.collapseBtn){
51915             this.collapseBtn.setVisible(c.collapsible == true);
51916         }
51917         this.cmargins = c.cmargins || this.cmargins ||
51918                          (this.position == "west" || this.position == "east" ?
51919                              {top: 0, left: 2, right:2, bottom: 0} :
51920                              {top: 2, left: 0, right:0, bottom: 2});
51921         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51922         this.bottomTabs = c.tabPosition != "top";
51923         this.autoScroll = c.autoScroll || false;
51924         if(this.autoScroll){
51925             this.bodyEl.setStyle("overflow", "auto");
51926         }else{
51927             this.bodyEl.setStyle("overflow", "hidden");
51928         }
51929         //if(c.titlebar !== false){
51930             if((!c.titlebar && !c.title) || c.titlebar === false){
51931                 this.titleEl.hide();
51932             }else{
51933                 this.titleEl.show();
51934                 if(c.title){
51935                     this.titleTextEl.innerHTML = c.title;
51936                 }
51937             }
51938         //}
51939         this.duration = c.duration || .30;
51940         this.slideDuration = c.slideDuration || .45;
51941         this.config = c;
51942         if(c.collapsed){
51943             this.collapse(true);
51944         }
51945         if(c.hidden){
51946             this.hide();
51947         }
51948     },
51949     /**
51950      * Returns true if this region is currently visible.
51951      * @return {Boolean}
51952      */
51953     isVisible : function(){
51954         return this.visible;
51955     },
51956
51957     /**
51958      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
51959      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
51960      */
51961     setCollapsedTitle : function(title){
51962         title = title || "&#160;";
51963         if(this.collapsedTitleTextEl){
51964             this.collapsedTitleTextEl.innerHTML = title;
51965         }
51966     },
51967
51968     getBox : function(){
51969         var b;
51970         if(!this.collapsed){
51971             b = this.el.getBox(false, true);
51972         }else{
51973             b = this.collapsedEl.getBox(false, true);
51974         }
51975         return b;
51976     },
51977
51978     getMargins : function(){
51979         return this.collapsed ? this.cmargins : this.margins;
51980     },
51981
51982     highlight : function(){
51983         this.el.addClass("x-layout-panel-dragover");
51984     },
51985
51986     unhighlight : function(){
51987         this.el.removeClass("x-layout-panel-dragover");
51988     },
51989
51990     updateBox : function(box){
51991         this.box = box;
51992         if(!this.collapsed){
51993             this.el.dom.style.left = box.x + "px";
51994             this.el.dom.style.top = box.y + "px";
51995             this.updateBody(box.width, box.height);
51996         }else{
51997             this.collapsedEl.dom.style.left = box.x + "px";
51998             this.collapsedEl.dom.style.top = box.y + "px";
51999             this.collapsedEl.setSize(box.width, box.height);
52000         }
52001         if(this.tabs){
52002             this.tabs.autoSizeTabs();
52003         }
52004     },
52005
52006     updateBody : function(w, h){
52007         if(w !== null){
52008             this.el.setWidth(w);
52009             w -= this.el.getBorderWidth("rl");
52010             if(this.config.adjustments){
52011                 w += this.config.adjustments[0];
52012             }
52013         }
52014         if(h !== null){
52015             this.el.setHeight(h);
52016             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52017             h -= this.el.getBorderWidth("tb");
52018             if(this.config.adjustments){
52019                 h += this.config.adjustments[1];
52020             }
52021             this.bodyEl.setHeight(h);
52022             if(this.tabs){
52023                 h = this.tabs.syncHeight(h);
52024             }
52025         }
52026         if(this.panelSize){
52027             w = w !== null ? w : this.panelSize.width;
52028             h = h !== null ? h : this.panelSize.height;
52029         }
52030         if(this.activePanel){
52031             var el = this.activePanel.getEl();
52032             w = w !== null ? w : el.getWidth();
52033             h = h !== null ? h : el.getHeight();
52034             this.panelSize = {width: w, height: h};
52035             this.activePanel.setSize(w, h);
52036         }
52037         if(Roo.isIE && this.tabs){
52038             this.tabs.el.repaint();
52039         }
52040     },
52041
52042     /**
52043      * Returns the container element for this region.
52044      * @return {Roo.Element}
52045      */
52046     getEl : function(){
52047         return this.el;
52048     },
52049
52050     /**
52051      * Hides this region.
52052      */
52053     hide : function(){
52054         if(!this.collapsed){
52055             this.el.dom.style.left = "-2000px";
52056             this.el.hide();
52057         }else{
52058             this.collapsedEl.dom.style.left = "-2000px";
52059             this.collapsedEl.hide();
52060         }
52061         this.visible = false;
52062         this.fireEvent("visibilitychange", this, false);
52063     },
52064
52065     /**
52066      * Shows this region if it was previously hidden.
52067      */
52068     show : function(){
52069         if(!this.collapsed){
52070             this.el.show();
52071         }else{
52072             this.collapsedEl.show();
52073         }
52074         this.visible = true;
52075         this.fireEvent("visibilitychange", this, true);
52076     },
52077
52078     closeClicked : function(){
52079         if(this.activePanel){
52080             this.remove(this.activePanel);
52081         }
52082     },
52083
52084     collapseClick : function(e){
52085         if(this.isSlid){
52086            e.stopPropagation();
52087            this.slideIn();
52088         }else{
52089            e.stopPropagation();
52090            this.slideOut();
52091         }
52092     },
52093
52094     /**
52095      * Collapses this region.
52096      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52097      */
52098     collapse : function(skipAnim, skipCheck = false){
52099         if(this.collapsed) {
52100             return;
52101         }
52102         
52103         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52104             
52105             this.collapsed = true;
52106             if(this.split){
52107                 this.split.el.hide();
52108             }
52109             if(this.config.animate && skipAnim !== true){
52110                 this.fireEvent("invalidated", this);
52111                 this.animateCollapse();
52112             }else{
52113                 this.el.setLocation(-20000,-20000);
52114                 this.el.hide();
52115                 this.collapsedEl.show();
52116                 this.fireEvent("collapsed", this);
52117                 this.fireEvent("invalidated", this);
52118             }
52119         }
52120         
52121     },
52122
52123     animateCollapse : function(){
52124         // overridden
52125     },
52126
52127     /**
52128      * Expands this region if it was previously collapsed.
52129      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52130      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52131      */
52132     expand : function(e, skipAnim){
52133         if(e) {
52134             e.stopPropagation();
52135         }
52136         if(!this.collapsed || this.el.hasActiveFx()) {
52137             return;
52138         }
52139         if(this.isSlid){
52140             this.afterSlideIn();
52141             skipAnim = true;
52142         }
52143         this.collapsed = false;
52144         if(this.config.animate && skipAnim !== true){
52145             this.animateExpand();
52146         }else{
52147             this.el.show();
52148             if(this.split){
52149                 this.split.el.show();
52150             }
52151             this.collapsedEl.setLocation(-2000,-2000);
52152             this.collapsedEl.hide();
52153             this.fireEvent("invalidated", this);
52154             this.fireEvent("expanded", this);
52155         }
52156     },
52157
52158     animateExpand : function(){
52159         // overridden
52160     },
52161
52162     initTabs : function()
52163     {
52164         this.bodyEl.setStyle("overflow", "hidden");
52165         var ts = new Roo.TabPanel(
52166                 this.bodyEl.dom,
52167                 {
52168                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52169                     disableTooltips: this.config.disableTabTips,
52170                     toolbar : this.config.toolbar
52171                 }
52172         );
52173         if(this.config.hideTabs){
52174             ts.stripWrap.setDisplayed(false);
52175         }
52176         this.tabs = ts;
52177         ts.resizeTabs = this.config.resizeTabs === true;
52178         ts.minTabWidth = this.config.minTabWidth || 40;
52179         ts.maxTabWidth = this.config.maxTabWidth || 250;
52180         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52181         ts.monitorResize = false;
52182         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52183         ts.bodyEl.addClass('x-layout-tabs-body');
52184         this.panels.each(this.initPanelAsTab, this);
52185     },
52186
52187     initPanelAsTab : function(panel){
52188         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52189                     this.config.closeOnTab && panel.isClosable());
52190         if(panel.tabTip !== undefined){
52191             ti.setTooltip(panel.tabTip);
52192         }
52193         ti.on("activate", function(){
52194               this.setActivePanel(panel);
52195         }, this);
52196         if(this.config.closeOnTab){
52197             ti.on("beforeclose", function(t, e){
52198                 e.cancel = true;
52199                 this.remove(panel);
52200             }, this);
52201         }
52202         return ti;
52203     },
52204
52205     updatePanelTitle : function(panel, title){
52206         if(this.activePanel == panel){
52207             this.updateTitle(title);
52208         }
52209         if(this.tabs){
52210             var ti = this.tabs.getTab(panel.getEl().id);
52211             ti.setText(title);
52212             if(panel.tabTip !== undefined){
52213                 ti.setTooltip(panel.tabTip);
52214             }
52215         }
52216     },
52217
52218     updateTitle : function(title){
52219         if(this.titleTextEl && !this.config.title){
52220             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52221         }
52222     },
52223
52224     setActivePanel : function(panel){
52225         panel = this.getPanel(panel);
52226         if(this.activePanel && this.activePanel != panel){
52227             this.activePanel.setActiveState(false);
52228         }
52229         this.activePanel = panel;
52230         panel.setActiveState(true);
52231         if(this.panelSize){
52232             panel.setSize(this.panelSize.width, this.panelSize.height);
52233         }
52234         if(this.closeBtn){
52235             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52236         }
52237         this.updateTitle(panel.getTitle());
52238         if(this.tabs){
52239             this.fireEvent("invalidated", this);
52240         }
52241         this.fireEvent("panelactivated", this, panel);
52242     },
52243
52244     /**
52245      * Shows the specified panel.
52246      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52247      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52248      */
52249     showPanel : function(panel)
52250     {
52251         panel = this.getPanel(panel);
52252         if(panel){
52253             if(this.tabs){
52254                 var tab = this.tabs.getTab(panel.getEl().id);
52255                 if(tab.isHidden()){
52256                     this.tabs.unhideTab(tab.id);
52257                 }
52258                 tab.activate();
52259             }else{
52260                 this.setActivePanel(panel);
52261             }
52262         }
52263         return panel;
52264     },
52265
52266     /**
52267      * Get the active panel for this region.
52268      * @return {Roo.ContentPanel} The active panel or null
52269      */
52270     getActivePanel : function(){
52271         return this.activePanel;
52272     },
52273
52274     validateVisibility : function(){
52275         if(this.panels.getCount() < 1){
52276             this.updateTitle("&#160;");
52277             this.closeBtn.hide();
52278             this.hide();
52279         }else{
52280             if(!this.isVisible()){
52281                 this.show();
52282             }
52283         }
52284     },
52285
52286     /**
52287      * Adds the passed ContentPanel(s) to this region.
52288      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52289      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52290      */
52291     add : function(panel){
52292         if(arguments.length > 1){
52293             for(var i = 0, len = arguments.length; i < len; i++) {
52294                 this.add(arguments[i]);
52295             }
52296             return null;
52297         }
52298         if(this.hasPanel(panel)){
52299             this.showPanel(panel);
52300             return panel;
52301         }
52302         panel.setRegion(this);
52303         this.panels.add(panel);
52304         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52305             this.bodyEl.dom.appendChild(panel.getEl().dom);
52306             if(panel.background !== true){
52307                 this.setActivePanel(panel);
52308             }
52309             this.fireEvent("paneladded", this, panel);
52310             return panel;
52311         }
52312         if(!this.tabs){
52313             this.initTabs();
52314         }else{
52315             this.initPanelAsTab(panel);
52316         }
52317         if(panel.background !== true){
52318             this.tabs.activate(panel.getEl().id);
52319         }
52320         this.fireEvent("paneladded", this, panel);
52321         return panel;
52322     },
52323
52324     /**
52325      * Hides the tab for the specified panel.
52326      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52327      */
52328     hidePanel : function(panel){
52329         if(this.tabs && (panel = this.getPanel(panel))){
52330             this.tabs.hideTab(panel.getEl().id);
52331         }
52332     },
52333
52334     /**
52335      * Unhides the tab for a previously hidden panel.
52336      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52337      */
52338     unhidePanel : function(panel){
52339         if(this.tabs && (panel = this.getPanel(panel))){
52340             this.tabs.unhideTab(panel.getEl().id);
52341         }
52342     },
52343
52344     clearPanels : function(){
52345         while(this.panels.getCount() > 0){
52346              this.remove(this.panels.first());
52347         }
52348     },
52349
52350     /**
52351      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52352      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52353      * @param {Boolean} preservePanel Overrides the config preservePanel option
52354      * @return {Roo.ContentPanel} The panel that was removed
52355      */
52356     remove : function(panel, preservePanel){
52357         panel = this.getPanel(panel);
52358         if(!panel){
52359             return null;
52360         }
52361         var e = {};
52362         this.fireEvent("beforeremove", this, panel, e);
52363         if(e.cancel === true){
52364             return null;
52365         }
52366         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52367         var panelId = panel.getId();
52368         this.panels.removeKey(panelId);
52369         if(preservePanel){
52370             document.body.appendChild(panel.getEl().dom);
52371         }
52372         if(this.tabs){
52373             this.tabs.removeTab(panel.getEl().id);
52374         }else if (!preservePanel){
52375             this.bodyEl.dom.removeChild(panel.getEl().dom);
52376         }
52377         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52378             var p = this.panels.first();
52379             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52380             tempEl.appendChild(p.getEl().dom);
52381             this.bodyEl.update("");
52382             this.bodyEl.dom.appendChild(p.getEl().dom);
52383             tempEl = null;
52384             this.updateTitle(p.getTitle());
52385             this.tabs = null;
52386             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52387             this.setActivePanel(p);
52388         }
52389         panel.setRegion(null);
52390         if(this.activePanel == panel){
52391             this.activePanel = null;
52392         }
52393         if(this.config.autoDestroy !== false && preservePanel !== true){
52394             try{panel.destroy();}catch(e){}
52395         }
52396         this.fireEvent("panelremoved", this, panel);
52397         return panel;
52398     },
52399
52400     /**
52401      * Returns the TabPanel component used by this region
52402      * @return {Roo.TabPanel}
52403      */
52404     getTabs : function(){
52405         return this.tabs;
52406     },
52407
52408     createTool : function(parentEl, className){
52409         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52410             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52411         btn.addClassOnOver("x-layout-tools-button-over");
52412         return btn;
52413     }
52414 });/*
52415  * Based on:
52416  * Ext JS Library 1.1.1
52417  * Copyright(c) 2006-2007, Ext JS, LLC.
52418  *
52419  * Originally Released Under LGPL - original licence link has changed is not relivant.
52420  *
52421  * Fork - LGPL
52422  * <script type="text/javascript">
52423  */
52424  
52425
52426
52427 /**
52428  * @class Roo.SplitLayoutRegion
52429  * @extends Roo.LayoutRegion
52430  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52431  */
52432 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52433     this.cursor = cursor;
52434     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52435 };
52436
52437 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52438     splitTip : "Drag to resize.",
52439     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52440     useSplitTips : false,
52441
52442     applyConfig : function(config){
52443         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52444         if(config.split){
52445             if(!this.split){
52446                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52447                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52448                 /** The SplitBar for this region 
52449                 * @type Roo.SplitBar */
52450                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52451                 this.split.on("moved", this.onSplitMove, this);
52452                 this.split.useShim = config.useShim === true;
52453                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52454                 if(this.useSplitTips){
52455                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52456                 }
52457                 if(config.collapsible){
52458                     this.split.el.on("dblclick", this.collapse,  this);
52459                 }
52460             }
52461             if(typeof config.minSize != "undefined"){
52462                 this.split.minSize = config.minSize;
52463             }
52464             if(typeof config.maxSize != "undefined"){
52465                 this.split.maxSize = config.maxSize;
52466             }
52467             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52468                 this.hideSplitter();
52469             }
52470         }
52471     },
52472
52473     getHMaxSize : function(){
52474          var cmax = this.config.maxSize || 10000;
52475          var center = this.mgr.getRegion("center");
52476          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52477     },
52478
52479     getVMaxSize : function(){
52480          var cmax = this.config.maxSize || 10000;
52481          var center = this.mgr.getRegion("center");
52482          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52483     },
52484
52485     onSplitMove : function(split, newSize){
52486         this.fireEvent("resized", this, newSize);
52487     },
52488     
52489     /** 
52490      * Returns the {@link Roo.SplitBar} for this region.
52491      * @return {Roo.SplitBar}
52492      */
52493     getSplitBar : function(){
52494         return this.split;
52495     },
52496     
52497     hide : function(){
52498         this.hideSplitter();
52499         Roo.SplitLayoutRegion.superclass.hide.call(this);
52500     },
52501
52502     hideSplitter : function(){
52503         if(this.split){
52504             this.split.el.setLocation(-2000,-2000);
52505             this.split.el.hide();
52506         }
52507     },
52508
52509     show : function(){
52510         if(this.split){
52511             this.split.el.show();
52512         }
52513         Roo.SplitLayoutRegion.superclass.show.call(this);
52514     },
52515     
52516     beforeSlide: function(){
52517         if(Roo.isGecko){// firefox overflow auto bug workaround
52518             this.bodyEl.clip();
52519             if(this.tabs) {
52520                 this.tabs.bodyEl.clip();
52521             }
52522             if(this.activePanel){
52523                 this.activePanel.getEl().clip();
52524                 
52525                 if(this.activePanel.beforeSlide){
52526                     this.activePanel.beforeSlide();
52527                 }
52528             }
52529         }
52530     },
52531     
52532     afterSlide : function(){
52533         if(Roo.isGecko){// firefox overflow auto bug workaround
52534             this.bodyEl.unclip();
52535             if(this.tabs) {
52536                 this.tabs.bodyEl.unclip();
52537             }
52538             if(this.activePanel){
52539                 this.activePanel.getEl().unclip();
52540                 if(this.activePanel.afterSlide){
52541                     this.activePanel.afterSlide();
52542                 }
52543             }
52544         }
52545     },
52546
52547     initAutoHide : function(){
52548         if(this.autoHide !== false){
52549             if(!this.autoHideHd){
52550                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52551                 this.autoHideHd = {
52552                     "mouseout": function(e){
52553                         if(!e.within(this.el, true)){
52554                             st.delay(500);
52555                         }
52556                     },
52557                     "mouseover" : function(e){
52558                         st.cancel();
52559                     },
52560                     scope : this
52561                 };
52562             }
52563             this.el.on(this.autoHideHd);
52564         }
52565     },
52566
52567     clearAutoHide : function(){
52568         if(this.autoHide !== false){
52569             this.el.un("mouseout", this.autoHideHd.mouseout);
52570             this.el.un("mouseover", this.autoHideHd.mouseover);
52571         }
52572     },
52573
52574     clearMonitor : function(){
52575         Roo.get(document).un("click", this.slideInIf, this);
52576     },
52577
52578     // these names are backwards but not changed for compat
52579     slideOut : function(){
52580         if(this.isSlid || this.el.hasActiveFx()){
52581             return;
52582         }
52583         this.isSlid = true;
52584         if(this.collapseBtn){
52585             this.collapseBtn.hide();
52586         }
52587         this.closeBtnState = this.closeBtn.getStyle('display');
52588         this.closeBtn.hide();
52589         if(this.stickBtn){
52590             this.stickBtn.show();
52591         }
52592         this.el.show();
52593         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52594         this.beforeSlide();
52595         this.el.setStyle("z-index", 10001);
52596         this.el.slideIn(this.getSlideAnchor(), {
52597             callback: function(){
52598                 this.afterSlide();
52599                 this.initAutoHide();
52600                 Roo.get(document).on("click", this.slideInIf, this);
52601                 this.fireEvent("slideshow", this);
52602             },
52603             scope: this,
52604             block: true
52605         });
52606     },
52607
52608     afterSlideIn : function(){
52609         this.clearAutoHide();
52610         this.isSlid = false;
52611         this.clearMonitor();
52612         this.el.setStyle("z-index", "");
52613         if(this.collapseBtn){
52614             this.collapseBtn.show();
52615         }
52616         this.closeBtn.setStyle('display', this.closeBtnState);
52617         if(this.stickBtn){
52618             this.stickBtn.hide();
52619         }
52620         this.fireEvent("slidehide", this);
52621     },
52622
52623     slideIn : function(cb){
52624         if(!this.isSlid || this.el.hasActiveFx()){
52625             Roo.callback(cb);
52626             return;
52627         }
52628         this.isSlid = false;
52629         this.beforeSlide();
52630         this.el.slideOut(this.getSlideAnchor(), {
52631             callback: function(){
52632                 this.el.setLeftTop(-10000, -10000);
52633                 this.afterSlide();
52634                 this.afterSlideIn();
52635                 Roo.callback(cb);
52636             },
52637             scope: this,
52638             block: true
52639         });
52640     },
52641     
52642     slideInIf : function(e){
52643         if(!e.within(this.el)){
52644             this.slideIn();
52645         }
52646     },
52647
52648     animateCollapse : function(){
52649         this.beforeSlide();
52650         this.el.setStyle("z-index", 20000);
52651         var anchor = this.getSlideAnchor();
52652         this.el.slideOut(anchor, {
52653             callback : function(){
52654                 this.el.setStyle("z-index", "");
52655                 this.collapsedEl.slideIn(anchor, {duration:.3});
52656                 this.afterSlide();
52657                 this.el.setLocation(-10000,-10000);
52658                 this.el.hide();
52659                 this.fireEvent("collapsed", this);
52660             },
52661             scope: this,
52662             block: true
52663         });
52664     },
52665
52666     animateExpand : function(){
52667         this.beforeSlide();
52668         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52669         this.el.setStyle("z-index", 20000);
52670         this.collapsedEl.hide({
52671             duration:.1
52672         });
52673         this.el.slideIn(this.getSlideAnchor(), {
52674             callback : function(){
52675                 this.el.setStyle("z-index", "");
52676                 this.afterSlide();
52677                 if(this.split){
52678                     this.split.el.show();
52679                 }
52680                 this.fireEvent("invalidated", this);
52681                 this.fireEvent("expanded", this);
52682             },
52683             scope: this,
52684             block: true
52685         });
52686     },
52687
52688     anchors : {
52689         "west" : "left",
52690         "east" : "right",
52691         "north" : "top",
52692         "south" : "bottom"
52693     },
52694
52695     sanchors : {
52696         "west" : "l",
52697         "east" : "r",
52698         "north" : "t",
52699         "south" : "b"
52700     },
52701
52702     canchors : {
52703         "west" : "tl-tr",
52704         "east" : "tr-tl",
52705         "north" : "tl-bl",
52706         "south" : "bl-tl"
52707     },
52708
52709     getAnchor : function(){
52710         return this.anchors[this.position];
52711     },
52712
52713     getCollapseAnchor : function(){
52714         return this.canchors[this.position];
52715     },
52716
52717     getSlideAnchor : function(){
52718         return this.sanchors[this.position];
52719     },
52720
52721     getAlignAdj : function(){
52722         var cm = this.cmargins;
52723         switch(this.position){
52724             case "west":
52725                 return [0, 0];
52726             break;
52727             case "east":
52728                 return [0, 0];
52729             break;
52730             case "north":
52731                 return [0, 0];
52732             break;
52733             case "south":
52734                 return [0, 0];
52735             break;
52736         }
52737     },
52738
52739     getExpandAdj : function(){
52740         var c = this.collapsedEl, cm = this.cmargins;
52741         switch(this.position){
52742             case "west":
52743                 return [-(cm.right+c.getWidth()+cm.left), 0];
52744             break;
52745             case "east":
52746                 return [cm.right+c.getWidth()+cm.left, 0];
52747             break;
52748             case "north":
52749                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52750             break;
52751             case "south":
52752                 return [0, cm.top+cm.bottom+c.getHeight()];
52753             break;
52754         }
52755     }
52756 });/*
52757  * Based on:
52758  * Ext JS Library 1.1.1
52759  * Copyright(c) 2006-2007, Ext JS, LLC.
52760  *
52761  * Originally Released Under LGPL - original licence link has changed is not relivant.
52762  *
52763  * Fork - LGPL
52764  * <script type="text/javascript">
52765  */
52766 /*
52767  * These classes are private internal classes
52768  */
52769 Roo.CenterLayoutRegion = function(mgr, config){
52770     Roo.LayoutRegion.call(this, mgr, config, "center");
52771     this.visible = true;
52772     this.minWidth = config.minWidth || 20;
52773     this.minHeight = config.minHeight || 20;
52774 };
52775
52776 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52777     hide : function(){
52778         // center panel can't be hidden
52779     },
52780     
52781     show : function(){
52782         // center panel can't be hidden
52783     },
52784     
52785     getMinWidth: function(){
52786         return this.minWidth;
52787     },
52788     
52789     getMinHeight: function(){
52790         return this.minHeight;
52791     }
52792 });
52793
52794
52795 Roo.NorthLayoutRegion = function(mgr, config){
52796     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52797     if(this.split){
52798         this.split.placement = Roo.SplitBar.TOP;
52799         this.split.orientation = Roo.SplitBar.VERTICAL;
52800         this.split.el.addClass("x-layout-split-v");
52801     }
52802     var size = config.initialSize || config.height;
52803     if(typeof size != "undefined"){
52804         this.el.setHeight(size);
52805     }
52806 };
52807 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52808     orientation: Roo.SplitBar.VERTICAL,
52809     getBox : function(){
52810         if(this.collapsed){
52811             return this.collapsedEl.getBox();
52812         }
52813         var box = this.el.getBox();
52814         if(this.split){
52815             box.height += this.split.el.getHeight();
52816         }
52817         return box;
52818     },
52819     
52820     updateBox : function(box){
52821         if(this.split && !this.collapsed){
52822             box.height -= this.split.el.getHeight();
52823             this.split.el.setLeft(box.x);
52824             this.split.el.setTop(box.y+box.height);
52825             this.split.el.setWidth(box.width);
52826         }
52827         if(this.collapsed){
52828             this.updateBody(box.width, null);
52829         }
52830         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52831     }
52832 });
52833
52834 Roo.SouthLayoutRegion = function(mgr, config){
52835     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52836     if(this.split){
52837         this.split.placement = Roo.SplitBar.BOTTOM;
52838         this.split.orientation = Roo.SplitBar.VERTICAL;
52839         this.split.el.addClass("x-layout-split-v");
52840     }
52841     var size = config.initialSize || config.height;
52842     if(typeof size != "undefined"){
52843         this.el.setHeight(size);
52844     }
52845 };
52846 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52847     orientation: Roo.SplitBar.VERTICAL,
52848     getBox : function(){
52849         if(this.collapsed){
52850             return this.collapsedEl.getBox();
52851         }
52852         var box = this.el.getBox();
52853         if(this.split){
52854             var sh = this.split.el.getHeight();
52855             box.height += sh;
52856             box.y -= sh;
52857         }
52858         return box;
52859     },
52860     
52861     updateBox : function(box){
52862         if(this.split && !this.collapsed){
52863             var sh = this.split.el.getHeight();
52864             box.height -= sh;
52865             box.y += sh;
52866             this.split.el.setLeft(box.x);
52867             this.split.el.setTop(box.y-sh);
52868             this.split.el.setWidth(box.width);
52869         }
52870         if(this.collapsed){
52871             this.updateBody(box.width, null);
52872         }
52873         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52874     }
52875 });
52876
52877 Roo.EastLayoutRegion = function(mgr, config){
52878     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52879     if(this.split){
52880         this.split.placement = Roo.SplitBar.RIGHT;
52881         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52882         this.split.el.addClass("x-layout-split-h");
52883     }
52884     var size = config.initialSize || config.width;
52885     if(typeof size != "undefined"){
52886         this.el.setWidth(size);
52887     }
52888 };
52889 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52890     orientation: Roo.SplitBar.HORIZONTAL,
52891     getBox : function(){
52892         if(this.collapsed){
52893             return this.collapsedEl.getBox();
52894         }
52895         var box = this.el.getBox();
52896         if(this.split){
52897             var sw = this.split.el.getWidth();
52898             box.width += sw;
52899             box.x -= sw;
52900         }
52901         return box;
52902     },
52903
52904     updateBox : function(box){
52905         if(this.split && !this.collapsed){
52906             var sw = this.split.el.getWidth();
52907             box.width -= sw;
52908             this.split.el.setLeft(box.x);
52909             this.split.el.setTop(box.y);
52910             this.split.el.setHeight(box.height);
52911             box.x += sw;
52912         }
52913         if(this.collapsed){
52914             this.updateBody(null, box.height);
52915         }
52916         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52917     }
52918 });
52919
52920 Roo.WestLayoutRegion = function(mgr, config){
52921     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52922     if(this.split){
52923         this.split.placement = Roo.SplitBar.LEFT;
52924         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52925         this.split.el.addClass("x-layout-split-h");
52926     }
52927     var size = config.initialSize || config.width;
52928     if(typeof size != "undefined"){
52929         this.el.setWidth(size);
52930     }
52931 };
52932 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52933     orientation: Roo.SplitBar.HORIZONTAL,
52934     getBox : function(){
52935         if(this.collapsed){
52936             return this.collapsedEl.getBox();
52937         }
52938         var box = this.el.getBox();
52939         if(this.split){
52940             box.width += this.split.el.getWidth();
52941         }
52942         return box;
52943     },
52944     
52945     updateBox : function(box){
52946         if(this.split && !this.collapsed){
52947             var sw = this.split.el.getWidth();
52948             box.width -= sw;
52949             this.split.el.setLeft(box.x+box.width);
52950             this.split.el.setTop(box.y);
52951             this.split.el.setHeight(box.height);
52952         }
52953         if(this.collapsed){
52954             this.updateBody(null, box.height);
52955         }
52956         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52957     }
52958 });
52959 /*
52960  * Based on:
52961  * Ext JS Library 1.1.1
52962  * Copyright(c) 2006-2007, Ext JS, LLC.
52963  *
52964  * Originally Released Under LGPL - original licence link has changed is not relivant.
52965  *
52966  * Fork - LGPL
52967  * <script type="text/javascript">
52968  */
52969  
52970  
52971 /*
52972  * Private internal class for reading and applying state
52973  */
52974 Roo.LayoutStateManager = function(layout){
52975      // default empty state
52976      this.state = {
52977         north: {},
52978         south: {},
52979         east: {},
52980         west: {}       
52981     };
52982 };
52983
52984 Roo.LayoutStateManager.prototype = {
52985     init : function(layout, provider){
52986         this.provider = provider;
52987         var state = provider.get(layout.id+"-layout-state");
52988         if(state){
52989             var wasUpdating = layout.isUpdating();
52990             if(!wasUpdating){
52991                 layout.beginUpdate();
52992             }
52993             for(var key in state){
52994                 if(typeof state[key] != "function"){
52995                     var rstate = state[key];
52996                     var r = layout.getRegion(key);
52997                     if(r && rstate){
52998                         if(rstate.size){
52999                             r.resizeTo(rstate.size);
53000                         }
53001                         if(rstate.collapsed == true){
53002                             r.collapse(true);
53003                         }else{
53004                             r.expand(null, true);
53005                         }
53006                     }
53007                 }
53008             }
53009             if(!wasUpdating){
53010                 layout.endUpdate();
53011             }
53012             this.state = state; 
53013         }
53014         this.layout = layout;
53015         layout.on("regionresized", this.onRegionResized, this);
53016         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53017         layout.on("regionexpanded", this.onRegionExpanded, this);
53018     },
53019     
53020     storeState : function(){
53021         this.provider.set(this.layout.id+"-layout-state", this.state);
53022     },
53023     
53024     onRegionResized : function(region, newSize){
53025         this.state[region.getPosition()].size = newSize;
53026         this.storeState();
53027     },
53028     
53029     onRegionCollapsed : function(region){
53030         this.state[region.getPosition()].collapsed = true;
53031         this.storeState();
53032     },
53033     
53034     onRegionExpanded : function(region){
53035         this.state[region.getPosition()].collapsed = false;
53036         this.storeState();
53037     }
53038 };/*
53039  * Based on:
53040  * Ext JS Library 1.1.1
53041  * Copyright(c) 2006-2007, Ext JS, LLC.
53042  *
53043  * Originally Released Under LGPL - original licence link has changed is not relivant.
53044  *
53045  * Fork - LGPL
53046  * <script type="text/javascript">
53047  */
53048 /**
53049  * @class Roo.ContentPanel
53050  * @extends Roo.util.Observable
53051  * A basic ContentPanel element.
53052  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53053  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53054  * @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
53055  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53056  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53057  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53058  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53059  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53060  * @cfg {String} title          The title for this panel
53061  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53062  * @cfg {String} url            Calls {@link #setUrl} with this value
53063  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53064  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53065  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53066  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53067
53068  * @constructor
53069  * Create a new ContentPanel.
53070  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53071  * @param {String/Object} config A string to set only the title or a config object
53072  * @param {String} content (optional) Set the HTML content for this panel
53073  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53074  */
53075 Roo.ContentPanel = function(el, config, content){
53076     
53077      
53078     /*
53079     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53080         config = el;
53081         el = Roo.id();
53082     }
53083     if (config && config.parentLayout) { 
53084         el = config.parentLayout.el.createChild(); 
53085     }
53086     */
53087     if(el.autoCreate){ // xtype is available if this is called from factory
53088         config = el;
53089         el = Roo.id();
53090     }
53091     this.el = Roo.get(el);
53092     if(!this.el && config && config.autoCreate){
53093         if(typeof config.autoCreate == "object"){
53094             if(!config.autoCreate.id){
53095                 config.autoCreate.id = config.id||el;
53096             }
53097             this.el = Roo.DomHelper.append(document.body,
53098                         config.autoCreate, true);
53099         }else{
53100             this.el = Roo.DomHelper.append(document.body,
53101                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53102         }
53103     }
53104     this.closable = false;
53105     this.loaded = false;
53106     this.active = false;
53107     if(typeof config == "string"){
53108         this.title = config;
53109     }else{
53110         Roo.apply(this, config);
53111     }
53112     
53113     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53114         this.wrapEl = this.el.wrap();
53115         this.toolbar.container = this.el.insertSibling(false, 'before');
53116         this.toolbar = new Roo.Toolbar(this.toolbar);
53117     }
53118     
53119     // xtype created footer. - not sure if will work as we normally have to render first..
53120     if (this.footer && !this.footer.el && this.footer.xtype) {
53121         if (!this.wrapEl) {
53122             this.wrapEl = this.el.wrap();
53123         }
53124     
53125         this.footer.container = this.wrapEl.createChild();
53126          
53127         this.footer = Roo.factory(this.footer, Roo);
53128         
53129     }
53130     
53131     if(this.resizeEl){
53132         this.resizeEl = Roo.get(this.resizeEl, true);
53133     }else{
53134         this.resizeEl = this.el;
53135     }
53136     // handle view.xtype
53137     
53138  
53139     
53140     
53141     this.addEvents({
53142         /**
53143          * @event activate
53144          * Fires when this panel is activated. 
53145          * @param {Roo.ContentPanel} this
53146          */
53147         "activate" : true,
53148         /**
53149          * @event deactivate
53150          * Fires when this panel is activated. 
53151          * @param {Roo.ContentPanel} this
53152          */
53153         "deactivate" : true,
53154
53155         /**
53156          * @event resize
53157          * Fires when this panel is resized if fitToFrame is true.
53158          * @param {Roo.ContentPanel} this
53159          * @param {Number} width The width after any component adjustments
53160          * @param {Number} height The height after any component adjustments
53161          */
53162         "resize" : true,
53163         
53164          /**
53165          * @event render
53166          * Fires when this tab is created
53167          * @param {Roo.ContentPanel} this
53168          */
53169         "render" : true
53170         
53171         
53172         
53173     });
53174     
53175
53176     
53177     
53178     if(this.autoScroll){
53179         this.resizeEl.setStyle("overflow", "auto");
53180     } else {
53181         // fix randome scrolling
53182         this.el.on('scroll', function() {
53183             Roo.log('fix random scolling');
53184             this.scrollTo('top',0); 
53185         });
53186     }
53187     content = content || this.content;
53188     if(content){
53189         this.setContent(content);
53190     }
53191     if(config && config.url){
53192         this.setUrl(this.url, this.params, this.loadOnce);
53193     }
53194     
53195     
53196     
53197     Roo.ContentPanel.superclass.constructor.call(this);
53198     
53199     if (this.view && typeof(this.view.xtype) != 'undefined') {
53200         this.view.el = this.el.appendChild(document.createElement("div"));
53201         this.view = Roo.factory(this.view); 
53202         this.view.render  &&  this.view.render(false, '');  
53203     }
53204     
53205     
53206     this.fireEvent('render', this);
53207 };
53208
53209 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53210     tabTip:'',
53211     setRegion : function(region){
53212         this.region = region;
53213         if(region){
53214            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53215         }else{
53216            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53217         } 
53218     },
53219     
53220     /**
53221      * Returns the toolbar for this Panel if one was configured. 
53222      * @return {Roo.Toolbar} 
53223      */
53224     getToolbar : function(){
53225         return this.toolbar;
53226     },
53227     
53228     setActiveState : function(active){
53229         this.active = active;
53230         if(!active){
53231             this.fireEvent("deactivate", this);
53232         }else{
53233             this.fireEvent("activate", this);
53234         }
53235     },
53236     /**
53237      * Updates this panel's element
53238      * @param {String} content The new content
53239      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53240     */
53241     setContent : function(content, loadScripts){
53242         this.el.update(content, loadScripts);
53243     },
53244
53245     ignoreResize : function(w, h){
53246         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53247             return true;
53248         }else{
53249             this.lastSize = {width: w, height: h};
53250             return false;
53251         }
53252     },
53253     /**
53254      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53255      * @return {Roo.UpdateManager} The UpdateManager
53256      */
53257     getUpdateManager : function(){
53258         return this.el.getUpdateManager();
53259     },
53260      /**
53261      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53262      * @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:
53263 <pre><code>
53264 panel.load({
53265     url: "your-url.php",
53266     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53267     callback: yourFunction,
53268     scope: yourObject, //(optional scope)
53269     discardUrl: false,
53270     nocache: false,
53271     text: "Loading...",
53272     timeout: 30,
53273     scripts: false
53274 });
53275 </code></pre>
53276      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53277      * 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.
53278      * @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}
53279      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53280      * @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.
53281      * @return {Roo.ContentPanel} this
53282      */
53283     load : function(){
53284         var um = this.el.getUpdateManager();
53285         um.update.apply(um, arguments);
53286         return this;
53287     },
53288
53289
53290     /**
53291      * 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.
53292      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53293      * @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)
53294      * @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)
53295      * @return {Roo.UpdateManager} The UpdateManager
53296      */
53297     setUrl : function(url, params, loadOnce){
53298         if(this.refreshDelegate){
53299             this.removeListener("activate", this.refreshDelegate);
53300         }
53301         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53302         this.on("activate", this.refreshDelegate);
53303         return this.el.getUpdateManager();
53304     },
53305     
53306     _handleRefresh : function(url, params, loadOnce){
53307         if(!loadOnce || !this.loaded){
53308             var updater = this.el.getUpdateManager();
53309             updater.update(url, params, this._setLoaded.createDelegate(this));
53310         }
53311     },
53312     
53313     _setLoaded : function(){
53314         this.loaded = true;
53315     }, 
53316     
53317     /**
53318      * Returns this panel's id
53319      * @return {String} 
53320      */
53321     getId : function(){
53322         return this.el.id;
53323     },
53324     
53325     /** 
53326      * Returns this panel's element - used by regiosn to add.
53327      * @return {Roo.Element} 
53328      */
53329     getEl : function(){
53330         return this.wrapEl || this.el;
53331     },
53332     
53333     adjustForComponents : function(width, height)
53334     {
53335         //Roo.log('adjustForComponents ');
53336         if(this.resizeEl != this.el){
53337             width -= this.el.getFrameWidth('lr');
53338             height -= this.el.getFrameWidth('tb');
53339         }
53340         if(this.toolbar){
53341             var te = this.toolbar.getEl();
53342             height -= te.getHeight();
53343             te.setWidth(width);
53344         }
53345         if(this.footer){
53346             var te = this.footer.getEl();
53347             Roo.log("footer:" + te.getHeight());
53348             
53349             height -= te.getHeight();
53350             te.setWidth(width);
53351         }
53352         
53353         
53354         if(this.adjustments){
53355             width += this.adjustments[0];
53356             height += this.adjustments[1];
53357         }
53358         return {"width": width, "height": height};
53359     },
53360     
53361     setSize : function(width, height){
53362         if(this.fitToFrame && !this.ignoreResize(width, height)){
53363             if(this.fitContainer && this.resizeEl != this.el){
53364                 this.el.setSize(width, height);
53365             }
53366             var size = this.adjustForComponents(width, height);
53367             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53368             this.fireEvent('resize', this, size.width, size.height);
53369         }
53370     },
53371     
53372     /**
53373      * Returns this panel's title
53374      * @return {String} 
53375      */
53376     getTitle : function(){
53377         return this.title;
53378     },
53379     
53380     /**
53381      * Set this panel's title
53382      * @param {String} title
53383      */
53384     setTitle : function(title){
53385         this.title = title;
53386         if(this.region){
53387             this.region.updatePanelTitle(this, title);
53388         }
53389     },
53390     
53391     /**
53392      * Returns true is this panel was configured to be closable
53393      * @return {Boolean} 
53394      */
53395     isClosable : function(){
53396         return this.closable;
53397     },
53398     
53399     beforeSlide : function(){
53400         this.el.clip();
53401         this.resizeEl.clip();
53402     },
53403     
53404     afterSlide : function(){
53405         this.el.unclip();
53406         this.resizeEl.unclip();
53407     },
53408     
53409     /**
53410      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53411      *   Will fail silently if the {@link #setUrl} method has not been called.
53412      *   This does not activate the panel, just updates its content.
53413      */
53414     refresh : function(){
53415         if(this.refreshDelegate){
53416            this.loaded = false;
53417            this.refreshDelegate();
53418         }
53419     },
53420     
53421     /**
53422      * Destroys this panel
53423      */
53424     destroy : function(){
53425         this.el.removeAllListeners();
53426         var tempEl = document.createElement("span");
53427         tempEl.appendChild(this.el.dom);
53428         tempEl.innerHTML = "";
53429         this.el.remove();
53430         this.el = null;
53431     },
53432     
53433     /**
53434      * form - if the content panel contains a form - this is a reference to it.
53435      * @type {Roo.form.Form}
53436      */
53437     form : false,
53438     /**
53439      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53440      *    This contains a reference to it.
53441      * @type {Roo.View}
53442      */
53443     view : false,
53444     
53445       /**
53446      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53447      * <pre><code>
53448
53449 layout.addxtype({
53450        xtype : 'Form',
53451        items: [ .... ]
53452    }
53453 );
53454
53455 </code></pre>
53456      * @param {Object} cfg Xtype definition of item to add.
53457      */
53458     
53459     addxtype : function(cfg) {
53460         // add form..
53461         if (cfg.xtype.match(/^Form$/)) {
53462             
53463             var el;
53464             //if (this.footer) {
53465             //    el = this.footer.container.insertSibling(false, 'before');
53466             //} else {
53467                 el = this.el.createChild();
53468             //}
53469
53470             this.form = new  Roo.form.Form(cfg);
53471             
53472             
53473             if ( this.form.allItems.length) {
53474                 this.form.render(el.dom);
53475             }
53476             return this.form;
53477         }
53478         // should only have one of theses..
53479         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53480             // views.. should not be just added - used named prop 'view''
53481             
53482             cfg.el = this.el.appendChild(document.createElement("div"));
53483             // factory?
53484             
53485             var ret = new Roo.factory(cfg);
53486              
53487              ret.render && ret.render(false, ''); // render blank..
53488             this.view = ret;
53489             return ret;
53490         }
53491         return false;
53492     }
53493 });
53494
53495 /**
53496  * @class Roo.GridPanel
53497  * @extends Roo.ContentPanel
53498  * @constructor
53499  * Create a new GridPanel.
53500  * @param {Roo.grid.Grid} grid The grid for this panel
53501  * @param {String/Object} config A string to set only the panel's title, or a config object
53502  */
53503 Roo.GridPanel = function(grid, config){
53504     
53505   
53506     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53507         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53508         
53509     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53510     
53511     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53512     
53513     if(this.toolbar){
53514         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53515     }
53516     // xtype created footer. - not sure if will work as we normally have to render first..
53517     if (this.footer && !this.footer.el && this.footer.xtype) {
53518         
53519         this.footer.container = this.grid.getView().getFooterPanel(true);
53520         this.footer.dataSource = this.grid.dataSource;
53521         this.footer = Roo.factory(this.footer, Roo);
53522         
53523     }
53524     
53525     grid.monitorWindowResize = false; // turn off autosizing
53526     grid.autoHeight = false;
53527     grid.autoWidth = false;
53528     this.grid = grid;
53529     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53530 };
53531
53532 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53533     getId : function(){
53534         return this.grid.id;
53535     },
53536     
53537     /**
53538      * Returns the grid for this panel
53539      * @return {Roo.grid.Grid} 
53540      */
53541     getGrid : function(){
53542         return this.grid;    
53543     },
53544     
53545     setSize : function(width, height){
53546         if(!this.ignoreResize(width, height)){
53547             var grid = this.grid;
53548             var size = this.adjustForComponents(width, height);
53549             grid.getGridEl().setSize(size.width, size.height);
53550             grid.autoSize();
53551         }
53552     },
53553     
53554     beforeSlide : function(){
53555         this.grid.getView().scroller.clip();
53556     },
53557     
53558     afterSlide : function(){
53559         this.grid.getView().scroller.unclip();
53560     },
53561     
53562     destroy : function(){
53563         this.grid.destroy();
53564         delete this.grid;
53565         Roo.GridPanel.superclass.destroy.call(this); 
53566     }
53567 });
53568
53569
53570 /**
53571  * @class Roo.NestedLayoutPanel
53572  * @extends Roo.ContentPanel
53573  * @constructor
53574  * Create a new NestedLayoutPanel.
53575  * 
53576  * 
53577  * @param {Roo.BorderLayout} layout The layout for this panel
53578  * @param {String/Object} config A string to set only the title or a config object
53579  */
53580 Roo.NestedLayoutPanel = function(layout, config)
53581 {
53582     // construct with only one argument..
53583     /* FIXME - implement nicer consturctors
53584     if (layout.layout) {
53585         config = layout;
53586         layout = config.layout;
53587         delete config.layout;
53588     }
53589     if (layout.xtype && !layout.getEl) {
53590         // then layout needs constructing..
53591         layout = Roo.factory(layout, Roo);
53592     }
53593     */
53594     
53595     
53596     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53597     
53598     layout.monitorWindowResize = false; // turn off autosizing
53599     this.layout = layout;
53600     this.layout.getEl().addClass("x-layout-nested-layout");
53601     
53602     
53603     
53604     
53605 };
53606
53607 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53608
53609     setSize : function(width, height){
53610         if(!this.ignoreResize(width, height)){
53611             var size = this.adjustForComponents(width, height);
53612             var el = this.layout.getEl();
53613             el.setSize(size.width, size.height);
53614             var touch = el.dom.offsetWidth;
53615             this.layout.layout();
53616             // ie requires a double layout on the first pass
53617             if(Roo.isIE && !this.initialized){
53618                 this.initialized = true;
53619                 this.layout.layout();
53620             }
53621         }
53622     },
53623     
53624     // activate all subpanels if not currently active..
53625     
53626     setActiveState : function(active){
53627         this.active = active;
53628         if(!active){
53629             this.fireEvent("deactivate", this);
53630             return;
53631         }
53632         
53633         this.fireEvent("activate", this);
53634         // not sure if this should happen before or after..
53635         if (!this.layout) {
53636             return; // should not happen..
53637         }
53638         var reg = false;
53639         for (var r in this.layout.regions) {
53640             reg = this.layout.getRegion(r);
53641             if (reg.getActivePanel()) {
53642                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53643                 reg.setActivePanel(reg.getActivePanel());
53644                 continue;
53645             }
53646             if (!reg.panels.length) {
53647                 continue;
53648             }
53649             reg.showPanel(reg.getPanel(0));
53650         }
53651         
53652         
53653         
53654         
53655     },
53656     
53657     /**
53658      * Returns the nested BorderLayout for this panel
53659      * @return {Roo.BorderLayout} 
53660      */
53661     getLayout : function(){
53662         return this.layout;
53663     },
53664     
53665      /**
53666      * Adds a xtype elements to the layout of the nested panel
53667      * <pre><code>
53668
53669 panel.addxtype({
53670        xtype : 'ContentPanel',
53671        region: 'west',
53672        items: [ .... ]
53673    }
53674 );
53675
53676 panel.addxtype({
53677         xtype : 'NestedLayoutPanel',
53678         region: 'west',
53679         layout: {
53680            center: { },
53681            west: { }   
53682         },
53683         items : [ ... list of content panels or nested layout panels.. ]
53684    }
53685 );
53686 </code></pre>
53687      * @param {Object} cfg Xtype definition of item to add.
53688      */
53689     addxtype : function(cfg) {
53690         return this.layout.addxtype(cfg);
53691     
53692     }
53693 });
53694
53695 Roo.ScrollPanel = function(el, config, content){
53696     config = config || {};
53697     config.fitToFrame = true;
53698     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53699     
53700     this.el.dom.style.overflow = "hidden";
53701     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53702     this.el.removeClass("x-layout-inactive-content");
53703     this.el.on("mousewheel", this.onWheel, this);
53704
53705     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53706     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53707     up.unselectable(); down.unselectable();
53708     up.on("click", this.scrollUp, this);
53709     down.on("click", this.scrollDown, this);
53710     up.addClassOnOver("x-scroller-btn-over");
53711     down.addClassOnOver("x-scroller-btn-over");
53712     up.addClassOnClick("x-scroller-btn-click");
53713     down.addClassOnClick("x-scroller-btn-click");
53714     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53715
53716     this.resizeEl = this.el;
53717     this.el = wrap; this.up = up; this.down = down;
53718 };
53719
53720 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53721     increment : 100,
53722     wheelIncrement : 5,
53723     scrollUp : function(){
53724         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53725     },
53726
53727     scrollDown : function(){
53728         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53729     },
53730
53731     afterScroll : function(){
53732         var el = this.resizeEl;
53733         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53734         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53735         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53736     },
53737
53738     setSize : function(){
53739         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53740         this.afterScroll();
53741     },
53742
53743     onWheel : function(e){
53744         var d = e.getWheelDelta();
53745         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53746         this.afterScroll();
53747         e.stopEvent();
53748     },
53749
53750     setContent : function(content, loadScripts){
53751         this.resizeEl.update(content, loadScripts);
53752     }
53753
53754 });
53755
53756
53757
53758
53759
53760
53761
53762
53763
53764 /**
53765  * @class Roo.TreePanel
53766  * @extends Roo.ContentPanel
53767  * @constructor
53768  * Create a new TreePanel. - defaults to fit/scoll contents.
53769  * @param {String/Object} config A string to set only the panel's title, or a config object
53770  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53771  */
53772 Roo.TreePanel = function(config){
53773     var el = config.el;
53774     var tree = config.tree;
53775     delete config.tree; 
53776     delete config.el; // hopefull!
53777     
53778     // wrapper for IE7 strict & safari scroll issue
53779     
53780     var treeEl = el.createChild();
53781     config.resizeEl = treeEl;
53782     
53783     
53784     
53785     Roo.TreePanel.superclass.constructor.call(this, el, config);
53786  
53787  
53788     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53789     //console.log(tree);
53790     this.on('activate', function()
53791     {
53792         if (this.tree.rendered) {
53793             return;
53794         }
53795         //console.log('render tree');
53796         this.tree.render();
53797     });
53798     // this should not be needed.. - it's actually the 'el' that resizes?
53799     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53800     
53801     //this.on('resize',  function (cp, w, h) {
53802     //        this.tree.innerCt.setWidth(w);
53803     //        this.tree.innerCt.setHeight(h);
53804     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53805     //});
53806
53807         
53808     
53809 };
53810
53811 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53812     fitToFrame : true,
53813     autoScroll : true
53814 });
53815
53816
53817
53818
53819
53820
53821
53822
53823
53824
53825
53826 /*
53827  * Based on:
53828  * Ext JS Library 1.1.1
53829  * Copyright(c) 2006-2007, Ext JS, LLC.
53830  *
53831  * Originally Released Under LGPL - original licence link has changed is not relivant.
53832  *
53833  * Fork - LGPL
53834  * <script type="text/javascript">
53835  */
53836  
53837
53838 /**
53839  * @class Roo.ReaderLayout
53840  * @extends Roo.BorderLayout
53841  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53842  * center region containing two nested regions (a top one for a list view and one for item preview below),
53843  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53844  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53845  * expedites the setup of the overall layout and regions for this common application style.
53846  * Example:
53847  <pre><code>
53848 var reader = new Roo.ReaderLayout();
53849 var CP = Roo.ContentPanel;  // shortcut for adding
53850
53851 reader.beginUpdate();
53852 reader.add("north", new CP("north", "North"));
53853 reader.add("west", new CP("west", {title: "West"}));
53854 reader.add("east", new CP("east", {title: "East"}));
53855
53856 reader.regions.listView.add(new CP("listView", "List"));
53857 reader.regions.preview.add(new CP("preview", "Preview"));
53858 reader.endUpdate();
53859 </code></pre>
53860 * @constructor
53861 * Create a new ReaderLayout
53862 * @param {Object} config Configuration options
53863 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53864 * document.body if omitted)
53865 */
53866 Roo.ReaderLayout = function(config, renderTo){
53867     var c = config || {size:{}};
53868     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53869         north: c.north !== false ? Roo.apply({
53870             split:false,
53871             initialSize: 32,
53872             titlebar: false
53873         }, c.north) : false,
53874         west: c.west !== false ? Roo.apply({
53875             split:true,
53876             initialSize: 200,
53877             minSize: 175,
53878             maxSize: 400,
53879             titlebar: true,
53880             collapsible: true,
53881             animate: true,
53882             margins:{left:5,right:0,bottom:5,top:5},
53883             cmargins:{left:5,right:5,bottom:5,top:5}
53884         }, c.west) : false,
53885         east: c.east !== false ? Roo.apply({
53886             split:true,
53887             initialSize: 200,
53888             minSize: 175,
53889             maxSize: 400,
53890             titlebar: true,
53891             collapsible: true,
53892             animate: true,
53893             margins:{left:0,right:5,bottom:5,top:5},
53894             cmargins:{left:5,right:5,bottom:5,top:5}
53895         }, c.east) : false,
53896         center: Roo.apply({
53897             tabPosition: 'top',
53898             autoScroll:false,
53899             closeOnTab: true,
53900             titlebar:false,
53901             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53902         }, c.center)
53903     });
53904
53905     this.el.addClass('x-reader');
53906
53907     this.beginUpdate();
53908
53909     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53910         south: c.preview !== false ? Roo.apply({
53911             split:true,
53912             initialSize: 200,
53913             minSize: 100,
53914             autoScroll:true,
53915             collapsible:true,
53916             titlebar: true,
53917             cmargins:{top:5,left:0, right:0, bottom:0}
53918         }, c.preview) : false,
53919         center: Roo.apply({
53920             autoScroll:false,
53921             titlebar:false,
53922             minHeight:200
53923         }, c.listView)
53924     });
53925     this.add('center', new Roo.NestedLayoutPanel(inner,
53926             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53927
53928     this.endUpdate();
53929
53930     this.regions.preview = inner.getRegion('south');
53931     this.regions.listView = inner.getRegion('center');
53932 };
53933
53934 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53935  * Based on:
53936  * Ext JS Library 1.1.1
53937  * Copyright(c) 2006-2007, Ext JS, LLC.
53938  *
53939  * Originally Released Under LGPL - original licence link has changed is not relivant.
53940  *
53941  * Fork - LGPL
53942  * <script type="text/javascript">
53943  */
53944  
53945 /**
53946  * @class Roo.grid.Grid
53947  * @extends Roo.util.Observable
53948  * This class represents the primary interface of a component based grid control.
53949  * <br><br>Usage:<pre><code>
53950  var grid = new Roo.grid.Grid("my-container-id", {
53951      ds: myDataStore,
53952      cm: myColModel,
53953      selModel: mySelectionModel,
53954      autoSizeColumns: true,
53955      monitorWindowResize: false,
53956      trackMouseOver: true
53957  });
53958  // set any options
53959  grid.render();
53960  * </code></pre>
53961  * <b>Common Problems:</b><br/>
53962  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
53963  * element will correct this<br/>
53964  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
53965  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
53966  * are unpredictable.<br/>
53967  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
53968  * grid to calculate dimensions/offsets.<br/>
53969   * @constructor
53970  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
53971  * The container MUST have some type of size defined for the grid to fill. The container will be
53972  * automatically set to position relative if it isn't already.
53973  * @param {Object} config A config object that sets properties on this grid.
53974  */
53975 Roo.grid.Grid = function(container, config){
53976         // initialize the container
53977         this.container = Roo.get(container);
53978         this.container.update("");
53979         this.container.setStyle("overflow", "hidden");
53980     this.container.addClass('x-grid-container');
53981
53982     this.id = this.container.id;
53983
53984     Roo.apply(this, config);
53985     // check and correct shorthanded configs
53986     if(this.ds){
53987         this.dataSource = this.ds;
53988         delete this.ds;
53989     }
53990     if(this.cm){
53991         this.colModel = this.cm;
53992         delete this.cm;
53993     }
53994     if(this.sm){
53995         this.selModel = this.sm;
53996         delete this.sm;
53997     }
53998
53999     if (this.selModel) {
54000         this.selModel = Roo.factory(this.selModel, Roo.grid);
54001         this.sm = this.selModel;
54002         this.sm.xmodule = this.xmodule || false;
54003     }
54004     if (typeof(this.colModel.config) == 'undefined') {
54005         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54006         this.cm = this.colModel;
54007         this.cm.xmodule = this.xmodule || false;
54008     }
54009     if (this.dataSource) {
54010         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54011         this.ds = this.dataSource;
54012         this.ds.xmodule = this.xmodule || false;
54013          
54014     }
54015     
54016     
54017     
54018     if(this.width){
54019         this.container.setWidth(this.width);
54020     }
54021
54022     if(this.height){
54023         this.container.setHeight(this.height);
54024     }
54025     /** @private */
54026         this.addEvents({
54027         // raw events
54028         /**
54029          * @event click
54030          * The raw click event for the entire grid.
54031          * @param {Roo.EventObject} e
54032          */
54033         "click" : true,
54034         /**
54035          * @event dblclick
54036          * The raw dblclick event for the entire grid.
54037          * @param {Roo.EventObject} e
54038          */
54039         "dblclick" : true,
54040         /**
54041          * @event contextmenu
54042          * The raw contextmenu event for the entire grid.
54043          * @param {Roo.EventObject} e
54044          */
54045         "contextmenu" : true,
54046         /**
54047          * @event mousedown
54048          * The raw mousedown event for the entire grid.
54049          * @param {Roo.EventObject} e
54050          */
54051         "mousedown" : true,
54052         /**
54053          * @event mouseup
54054          * The raw mouseup event for the entire grid.
54055          * @param {Roo.EventObject} e
54056          */
54057         "mouseup" : true,
54058         /**
54059          * @event mouseover
54060          * The raw mouseover event for the entire grid.
54061          * @param {Roo.EventObject} e
54062          */
54063         "mouseover" : true,
54064         /**
54065          * @event mouseout
54066          * The raw mouseout event for the entire grid.
54067          * @param {Roo.EventObject} e
54068          */
54069         "mouseout" : true,
54070         /**
54071          * @event keypress
54072          * The raw keypress event for the entire grid.
54073          * @param {Roo.EventObject} e
54074          */
54075         "keypress" : true,
54076         /**
54077          * @event keydown
54078          * The raw keydown event for the entire grid.
54079          * @param {Roo.EventObject} e
54080          */
54081         "keydown" : true,
54082
54083         // custom events
54084
54085         /**
54086          * @event cellclick
54087          * Fires when a cell is clicked
54088          * @param {Grid} this
54089          * @param {Number} rowIndex
54090          * @param {Number} columnIndex
54091          * @param {Roo.EventObject} e
54092          */
54093         "cellclick" : true,
54094         /**
54095          * @event celldblclick
54096          * Fires when a cell is double clicked
54097          * @param {Grid} this
54098          * @param {Number} rowIndex
54099          * @param {Number} columnIndex
54100          * @param {Roo.EventObject} e
54101          */
54102         "celldblclick" : true,
54103         /**
54104          * @event rowclick
54105          * Fires when a row is clicked
54106          * @param {Grid} this
54107          * @param {Number} rowIndex
54108          * @param {Roo.EventObject} e
54109          */
54110         "rowclick" : true,
54111         /**
54112          * @event rowdblclick
54113          * Fires when a row is double clicked
54114          * @param {Grid} this
54115          * @param {Number} rowIndex
54116          * @param {Roo.EventObject} e
54117          */
54118         "rowdblclick" : true,
54119         /**
54120          * @event headerclick
54121          * Fires when a header is clicked
54122          * @param {Grid} this
54123          * @param {Number} columnIndex
54124          * @param {Roo.EventObject} e
54125          */
54126         "headerclick" : true,
54127         /**
54128          * @event headerdblclick
54129          * Fires when a header cell is double clicked
54130          * @param {Grid} this
54131          * @param {Number} columnIndex
54132          * @param {Roo.EventObject} e
54133          */
54134         "headerdblclick" : true,
54135         /**
54136          * @event rowcontextmenu
54137          * Fires when a row is right clicked
54138          * @param {Grid} this
54139          * @param {Number} rowIndex
54140          * @param {Roo.EventObject} e
54141          */
54142         "rowcontextmenu" : true,
54143         /**
54144          * @event cellcontextmenu
54145          * Fires when a cell is right clicked
54146          * @param {Grid} this
54147          * @param {Number} rowIndex
54148          * @param {Number} cellIndex
54149          * @param {Roo.EventObject} e
54150          */
54151          "cellcontextmenu" : true,
54152         /**
54153          * @event headercontextmenu
54154          * Fires when a header is right clicked
54155          * @param {Grid} this
54156          * @param {Number} columnIndex
54157          * @param {Roo.EventObject} e
54158          */
54159         "headercontextmenu" : true,
54160         /**
54161          * @event bodyscroll
54162          * Fires when the body element is scrolled
54163          * @param {Number} scrollLeft
54164          * @param {Number} scrollTop
54165          */
54166         "bodyscroll" : true,
54167         /**
54168          * @event columnresize
54169          * Fires when the user resizes a column
54170          * @param {Number} columnIndex
54171          * @param {Number} newSize
54172          */
54173         "columnresize" : true,
54174         /**
54175          * @event columnmove
54176          * Fires when the user moves a column
54177          * @param {Number} oldIndex
54178          * @param {Number} newIndex
54179          */
54180         "columnmove" : true,
54181         /**
54182          * @event startdrag
54183          * Fires when row(s) start being dragged
54184          * @param {Grid} this
54185          * @param {Roo.GridDD} dd The drag drop object
54186          * @param {event} e The raw browser event
54187          */
54188         "startdrag" : true,
54189         /**
54190          * @event enddrag
54191          * Fires when a drag operation is complete
54192          * @param {Grid} this
54193          * @param {Roo.GridDD} dd The drag drop object
54194          * @param {event} e The raw browser event
54195          */
54196         "enddrag" : true,
54197         /**
54198          * @event dragdrop
54199          * Fires when dragged row(s) are dropped on a valid DD target
54200          * @param {Grid} this
54201          * @param {Roo.GridDD} dd The drag drop object
54202          * @param {String} targetId The target drag drop object
54203          * @param {event} e The raw browser event
54204          */
54205         "dragdrop" : true,
54206         /**
54207          * @event dragover
54208          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54209          * @param {Grid} this
54210          * @param {Roo.GridDD} dd The drag drop object
54211          * @param {String} targetId The target drag drop object
54212          * @param {event} e The raw browser event
54213          */
54214         "dragover" : true,
54215         /**
54216          * @event dragenter
54217          *  Fires when the dragged row(s) first cross another DD target while being dragged
54218          * @param {Grid} this
54219          * @param {Roo.GridDD} dd The drag drop object
54220          * @param {String} targetId The target drag drop object
54221          * @param {event} e The raw browser event
54222          */
54223         "dragenter" : true,
54224         /**
54225          * @event dragout
54226          * Fires when the dragged row(s) leave another DD target while being dragged
54227          * @param {Grid} this
54228          * @param {Roo.GridDD} dd The drag drop object
54229          * @param {String} targetId The target drag drop object
54230          * @param {event} e The raw browser event
54231          */
54232         "dragout" : true,
54233         /**
54234          * @event rowclass
54235          * Fires when a row is rendered, so you can change add a style to it.
54236          * @param {GridView} gridview   The grid view
54237          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54238          */
54239         'rowclass' : true,
54240
54241         /**
54242          * @event render
54243          * Fires when the grid is rendered
54244          * @param {Grid} grid
54245          */
54246         'render' : true
54247     });
54248
54249     Roo.grid.Grid.superclass.constructor.call(this);
54250 };
54251 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54252     
54253     /**
54254      * @cfg {String} ddGroup - drag drop group.
54255      */
54256
54257     /**
54258      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54259      */
54260     minColumnWidth : 25,
54261
54262     /**
54263      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54264      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54265      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54266      */
54267     autoSizeColumns : false,
54268
54269     /**
54270      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54271      */
54272     autoSizeHeaders : true,
54273
54274     /**
54275      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54276      */
54277     monitorWindowResize : true,
54278
54279     /**
54280      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54281      * rows measured to get a columns size. Default is 0 (all rows).
54282      */
54283     maxRowsToMeasure : 0,
54284
54285     /**
54286      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54287      */
54288     trackMouseOver : true,
54289
54290     /**
54291     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54292     */
54293     
54294     /**
54295     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54296     */
54297     enableDragDrop : false,
54298     
54299     /**
54300     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54301     */
54302     enableColumnMove : true,
54303     
54304     /**
54305     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54306     */
54307     enableColumnHide : true,
54308     
54309     /**
54310     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54311     */
54312     enableRowHeightSync : false,
54313     
54314     /**
54315     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54316     */
54317     stripeRows : true,
54318     
54319     /**
54320     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54321     */
54322     autoHeight : false,
54323
54324     /**
54325      * @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.
54326      */
54327     autoExpandColumn : false,
54328
54329     /**
54330     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54331     * Default is 50.
54332     */
54333     autoExpandMin : 50,
54334
54335     /**
54336     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54337     */
54338     autoExpandMax : 1000,
54339
54340     /**
54341     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54342     */
54343     view : null,
54344
54345     /**
54346     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54347     */
54348     loadMask : false,
54349     /**
54350     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54351     */
54352     dropTarget: false,
54353     
54354    
54355     
54356     // private
54357     rendered : false,
54358
54359     /**
54360     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54361     * of a fixed width. Default is false.
54362     */
54363     /**
54364     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54365     */
54366     /**
54367      * Called once after all setup has been completed and the grid is ready to be rendered.
54368      * @return {Roo.grid.Grid} this
54369      */
54370     render : function()
54371     {
54372         var c = this.container;
54373         // try to detect autoHeight/width mode
54374         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54375             this.autoHeight = true;
54376         }
54377         var view = this.getView();
54378         view.init(this);
54379
54380         c.on("click", this.onClick, this);
54381         c.on("dblclick", this.onDblClick, this);
54382         c.on("contextmenu", this.onContextMenu, this);
54383         c.on("keydown", this.onKeyDown, this);
54384         if (Roo.isTouch) {
54385             c.on("touchstart", this.onTouchStart, this);
54386         }
54387
54388         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54389
54390         this.getSelectionModel().init(this);
54391
54392         view.render();
54393
54394         if(this.loadMask){
54395             this.loadMask = new Roo.LoadMask(this.container,
54396                     Roo.apply({store:this.dataSource}, this.loadMask));
54397         }
54398         
54399         
54400         if (this.toolbar && this.toolbar.xtype) {
54401             this.toolbar.container = this.getView().getHeaderPanel(true);
54402             this.toolbar = new Roo.Toolbar(this.toolbar);
54403         }
54404         if (this.footer && this.footer.xtype) {
54405             this.footer.dataSource = this.getDataSource();
54406             this.footer.container = this.getView().getFooterPanel(true);
54407             this.footer = Roo.factory(this.footer, Roo);
54408         }
54409         if (this.dropTarget && this.dropTarget.xtype) {
54410             delete this.dropTarget.xtype;
54411             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54412         }
54413         
54414         
54415         this.rendered = true;
54416         this.fireEvent('render', this);
54417         return this;
54418     },
54419
54420         /**
54421          * Reconfigures the grid to use a different Store and Column Model.
54422          * The View will be bound to the new objects and refreshed.
54423          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54424          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54425          */
54426     reconfigure : function(dataSource, colModel){
54427         if(this.loadMask){
54428             this.loadMask.destroy();
54429             this.loadMask = new Roo.LoadMask(this.container,
54430                     Roo.apply({store:dataSource}, this.loadMask));
54431         }
54432         this.view.bind(dataSource, colModel);
54433         this.dataSource = dataSource;
54434         this.colModel = colModel;
54435         this.view.refresh(true);
54436     },
54437
54438     // private
54439     onKeyDown : function(e){
54440         this.fireEvent("keydown", e);
54441     },
54442
54443     /**
54444      * Destroy this grid.
54445      * @param {Boolean} removeEl True to remove the element
54446      */
54447     destroy : function(removeEl, keepListeners){
54448         if(this.loadMask){
54449             this.loadMask.destroy();
54450         }
54451         var c = this.container;
54452         c.removeAllListeners();
54453         this.view.destroy();
54454         this.colModel.purgeListeners();
54455         if(!keepListeners){
54456             this.purgeListeners();
54457         }
54458         c.update("");
54459         if(removeEl === true){
54460             c.remove();
54461         }
54462     },
54463
54464     // private
54465     processEvent : function(name, e){
54466         // does this fire select???
54467         //Roo.log('grid:processEvent '  + name);
54468         
54469         if (name != 'touchstart' ) {
54470             this.fireEvent(name, e);    
54471         }
54472         
54473         var t = e.getTarget();
54474         var v = this.view;
54475         var header = v.findHeaderIndex(t);
54476         if(header !== false){
54477             var ename = name == 'touchstart' ? 'click' : name;
54478              
54479             this.fireEvent("header" + ename, this, header, e);
54480         }else{
54481             var row = v.findRowIndex(t);
54482             var cell = v.findCellIndex(t);
54483             if (name == 'touchstart') {
54484                 // first touch is always a click.
54485                 // hopefull this happens after selection is updated.?
54486                 name = false;
54487                 
54488                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54489                     var cs = this.selModel.getSelectedCell();
54490                     if (row == cs[0] && cell == cs[1]){
54491                         name = 'dblclick';
54492                     }
54493                 }
54494                 if (typeof(this.selModel.getSelections) != 'undefined') {
54495                     var cs = this.selModel.getSelections();
54496                     var ds = this.dataSource;
54497                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54498                         name = 'dblclick';
54499                     }
54500                 }
54501                 if (!name) {
54502                     return;
54503                 }
54504             }
54505             
54506             
54507             if(row !== false){
54508                 this.fireEvent("row" + name, this, row, e);
54509                 if(cell !== false){
54510                     this.fireEvent("cell" + name, this, row, cell, e);
54511                 }
54512             }
54513         }
54514     },
54515
54516     // private
54517     onClick : function(e){
54518         this.processEvent("click", e);
54519     },
54520    // private
54521     onTouchStart : function(e){
54522         this.processEvent("touchstart", e);
54523     },
54524
54525     // private
54526     onContextMenu : function(e, t){
54527         this.processEvent("contextmenu", e);
54528     },
54529
54530     // private
54531     onDblClick : function(e){
54532         this.processEvent("dblclick", e);
54533     },
54534
54535     // private
54536     walkCells : function(row, col, step, fn, scope){
54537         var cm = this.colModel, clen = cm.getColumnCount();
54538         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54539         if(step < 0){
54540             if(col < 0){
54541                 row--;
54542                 first = false;
54543             }
54544             while(row >= 0){
54545                 if(!first){
54546                     col = clen-1;
54547                 }
54548                 first = false;
54549                 while(col >= 0){
54550                     if(fn.call(scope || this, row, col, cm) === true){
54551                         return [row, col];
54552                     }
54553                     col--;
54554                 }
54555                 row--;
54556             }
54557         } else {
54558             if(col >= clen){
54559                 row++;
54560                 first = false;
54561             }
54562             while(row < rlen){
54563                 if(!first){
54564                     col = 0;
54565                 }
54566                 first = false;
54567                 while(col < clen){
54568                     if(fn.call(scope || this, row, col, cm) === true){
54569                         return [row, col];
54570                     }
54571                     col++;
54572                 }
54573                 row++;
54574             }
54575         }
54576         return null;
54577     },
54578
54579     // private
54580     getSelections : function(){
54581         return this.selModel.getSelections();
54582     },
54583
54584     /**
54585      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54586      * but if manual update is required this method will initiate it.
54587      */
54588     autoSize : function(){
54589         if(this.rendered){
54590             this.view.layout();
54591             if(this.view.adjustForScroll){
54592                 this.view.adjustForScroll();
54593             }
54594         }
54595     },
54596
54597     /**
54598      * Returns the grid's underlying element.
54599      * @return {Element} The element
54600      */
54601     getGridEl : function(){
54602         return this.container;
54603     },
54604
54605     // private for compatibility, overridden by editor grid
54606     stopEditing : function(){},
54607
54608     /**
54609      * Returns the grid's SelectionModel.
54610      * @return {SelectionModel}
54611      */
54612     getSelectionModel : function(){
54613         if(!this.selModel){
54614             this.selModel = new Roo.grid.RowSelectionModel();
54615         }
54616         return this.selModel;
54617     },
54618
54619     /**
54620      * Returns the grid's DataSource.
54621      * @return {DataSource}
54622      */
54623     getDataSource : function(){
54624         return this.dataSource;
54625     },
54626
54627     /**
54628      * Returns the grid's ColumnModel.
54629      * @return {ColumnModel}
54630      */
54631     getColumnModel : function(){
54632         return this.colModel;
54633     },
54634
54635     /**
54636      * Returns the grid's GridView object.
54637      * @return {GridView}
54638      */
54639     getView : function(){
54640         if(!this.view){
54641             this.view = new Roo.grid.GridView(this.viewConfig);
54642         }
54643         return this.view;
54644     },
54645     /**
54646      * Called to get grid's drag proxy text, by default returns this.ddText.
54647      * @return {String}
54648      */
54649     getDragDropText : function(){
54650         var count = this.selModel.getCount();
54651         return String.format(this.ddText, count, count == 1 ? '' : 's');
54652     }
54653 });
54654 /**
54655  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54656  * %0 is replaced with the number of selected rows.
54657  * @type String
54658  */
54659 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54660  * Based on:
54661  * Ext JS Library 1.1.1
54662  * Copyright(c) 2006-2007, Ext JS, LLC.
54663  *
54664  * Originally Released Under LGPL - original licence link has changed is not relivant.
54665  *
54666  * Fork - LGPL
54667  * <script type="text/javascript">
54668  */
54669  
54670 Roo.grid.AbstractGridView = function(){
54671         this.grid = null;
54672         
54673         this.events = {
54674             "beforerowremoved" : true,
54675             "beforerowsinserted" : true,
54676             "beforerefresh" : true,
54677             "rowremoved" : true,
54678             "rowsinserted" : true,
54679             "rowupdated" : true,
54680             "refresh" : true
54681         };
54682     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54683 };
54684
54685 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54686     rowClass : "x-grid-row",
54687     cellClass : "x-grid-cell",
54688     tdClass : "x-grid-td",
54689     hdClass : "x-grid-hd",
54690     splitClass : "x-grid-hd-split",
54691     
54692     init: function(grid){
54693         this.grid = grid;
54694                 var cid = this.grid.getGridEl().id;
54695         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54696         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54697         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54698         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54699         },
54700         
54701     getColumnRenderers : function(){
54702         var renderers = [];
54703         var cm = this.grid.colModel;
54704         var colCount = cm.getColumnCount();
54705         for(var i = 0; i < colCount; i++){
54706             renderers[i] = cm.getRenderer(i);
54707         }
54708         return renderers;
54709     },
54710     
54711     getColumnIds : function(){
54712         var ids = [];
54713         var cm = this.grid.colModel;
54714         var colCount = cm.getColumnCount();
54715         for(var i = 0; i < colCount; i++){
54716             ids[i] = cm.getColumnId(i);
54717         }
54718         return ids;
54719     },
54720     
54721     getDataIndexes : function(){
54722         if(!this.indexMap){
54723             this.indexMap = this.buildIndexMap();
54724         }
54725         return this.indexMap.colToData;
54726     },
54727     
54728     getColumnIndexByDataIndex : function(dataIndex){
54729         if(!this.indexMap){
54730             this.indexMap = this.buildIndexMap();
54731         }
54732         return this.indexMap.dataToCol[dataIndex];
54733     },
54734     
54735     /**
54736      * Set a css style for a column dynamically. 
54737      * @param {Number} colIndex The index of the column
54738      * @param {String} name The css property name
54739      * @param {String} value The css value
54740      */
54741     setCSSStyle : function(colIndex, name, value){
54742         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54743         Roo.util.CSS.updateRule(selector, name, value);
54744     },
54745     
54746     generateRules : function(cm){
54747         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54748         Roo.util.CSS.removeStyleSheet(rulesId);
54749         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54750             var cid = cm.getColumnId(i);
54751             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54752                          this.tdSelector, cid, " {\n}\n",
54753                          this.hdSelector, cid, " {\n}\n",
54754                          this.splitSelector, cid, " {\n}\n");
54755         }
54756         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54757     }
54758 });/*
54759  * Based on:
54760  * Ext JS Library 1.1.1
54761  * Copyright(c) 2006-2007, Ext JS, LLC.
54762  *
54763  * Originally Released Under LGPL - original licence link has changed is not relivant.
54764  *
54765  * Fork - LGPL
54766  * <script type="text/javascript">
54767  */
54768
54769 // private
54770 // This is a support class used internally by the Grid components
54771 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54772     this.grid = grid;
54773     this.view = grid.getView();
54774     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54775     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54776     if(hd2){
54777         this.setHandleElId(Roo.id(hd));
54778         this.setOuterHandleElId(Roo.id(hd2));
54779     }
54780     this.scroll = false;
54781 };
54782 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54783     maxDragWidth: 120,
54784     getDragData : function(e){
54785         var t = Roo.lib.Event.getTarget(e);
54786         var h = this.view.findHeaderCell(t);
54787         if(h){
54788             return {ddel: h.firstChild, header:h};
54789         }
54790         return false;
54791     },
54792
54793     onInitDrag : function(e){
54794         this.view.headersDisabled = true;
54795         var clone = this.dragData.ddel.cloneNode(true);
54796         clone.id = Roo.id();
54797         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54798         this.proxy.update(clone);
54799         return true;
54800     },
54801
54802     afterValidDrop : function(){
54803         var v = this.view;
54804         setTimeout(function(){
54805             v.headersDisabled = false;
54806         }, 50);
54807     },
54808
54809     afterInvalidDrop : function(){
54810         var v = this.view;
54811         setTimeout(function(){
54812             v.headersDisabled = false;
54813         }, 50);
54814     }
54815 });
54816 /*
54817  * Based on:
54818  * Ext JS Library 1.1.1
54819  * Copyright(c) 2006-2007, Ext JS, LLC.
54820  *
54821  * Originally Released Under LGPL - original licence link has changed is not relivant.
54822  *
54823  * Fork - LGPL
54824  * <script type="text/javascript">
54825  */
54826 // private
54827 // This is a support class used internally by the Grid components
54828 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54829     this.grid = grid;
54830     this.view = grid.getView();
54831     // split the proxies so they don't interfere with mouse events
54832     this.proxyTop = Roo.DomHelper.append(document.body, {
54833         cls:"col-move-top", html:"&#160;"
54834     }, true);
54835     this.proxyBottom = Roo.DomHelper.append(document.body, {
54836         cls:"col-move-bottom", html:"&#160;"
54837     }, true);
54838     this.proxyTop.hide = this.proxyBottom.hide = function(){
54839         this.setLeftTop(-100,-100);
54840         this.setStyle("visibility", "hidden");
54841     };
54842     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54843     // temporarily disabled
54844     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54845     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54846 };
54847 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54848     proxyOffsets : [-4, -9],
54849     fly: Roo.Element.fly,
54850
54851     getTargetFromEvent : function(e){
54852         var t = Roo.lib.Event.getTarget(e);
54853         var cindex = this.view.findCellIndex(t);
54854         if(cindex !== false){
54855             return this.view.getHeaderCell(cindex);
54856         }
54857         return null;
54858     },
54859
54860     nextVisible : function(h){
54861         var v = this.view, cm = this.grid.colModel;
54862         h = h.nextSibling;
54863         while(h){
54864             if(!cm.isHidden(v.getCellIndex(h))){
54865                 return h;
54866             }
54867             h = h.nextSibling;
54868         }
54869         return null;
54870     },
54871
54872     prevVisible : function(h){
54873         var v = this.view, cm = this.grid.colModel;
54874         h = h.prevSibling;
54875         while(h){
54876             if(!cm.isHidden(v.getCellIndex(h))){
54877                 return h;
54878             }
54879             h = h.prevSibling;
54880         }
54881         return null;
54882     },
54883
54884     positionIndicator : function(h, n, e){
54885         var x = Roo.lib.Event.getPageX(e);
54886         var r = Roo.lib.Dom.getRegion(n.firstChild);
54887         var px, pt, py = r.top + this.proxyOffsets[1];
54888         if((r.right - x) <= (r.right-r.left)/2){
54889             px = r.right+this.view.borderWidth;
54890             pt = "after";
54891         }else{
54892             px = r.left;
54893             pt = "before";
54894         }
54895         var oldIndex = this.view.getCellIndex(h);
54896         var newIndex = this.view.getCellIndex(n);
54897
54898         if(this.grid.colModel.isFixed(newIndex)){
54899             return false;
54900         }
54901
54902         var locked = this.grid.colModel.isLocked(newIndex);
54903
54904         if(pt == "after"){
54905             newIndex++;
54906         }
54907         if(oldIndex < newIndex){
54908             newIndex--;
54909         }
54910         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54911             return false;
54912         }
54913         px +=  this.proxyOffsets[0];
54914         this.proxyTop.setLeftTop(px, py);
54915         this.proxyTop.show();
54916         if(!this.bottomOffset){
54917             this.bottomOffset = this.view.mainHd.getHeight();
54918         }
54919         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54920         this.proxyBottom.show();
54921         return pt;
54922     },
54923
54924     onNodeEnter : function(n, dd, e, data){
54925         if(data.header != n){
54926             this.positionIndicator(data.header, n, e);
54927         }
54928     },
54929
54930     onNodeOver : function(n, dd, e, data){
54931         var result = false;
54932         if(data.header != n){
54933             result = this.positionIndicator(data.header, n, e);
54934         }
54935         if(!result){
54936             this.proxyTop.hide();
54937             this.proxyBottom.hide();
54938         }
54939         return result ? this.dropAllowed : this.dropNotAllowed;
54940     },
54941
54942     onNodeOut : function(n, dd, e, data){
54943         this.proxyTop.hide();
54944         this.proxyBottom.hide();
54945     },
54946
54947     onNodeDrop : function(n, dd, e, data){
54948         var h = data.header;
54949         if(h != n){
54950             var cm = this.grid.colModel;
54951             var x = Roo.lib.Event.getPageX(e);
54952             var r = Roo.lib.Dom.getRegion(n.firstChild);
54953             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
54954             var oldIndex = this.view.getCellIndex(h);
54955             var newIndex = this.view.getCellIndex(n);
54956             var locked = cm.isLocked(newIndex);
54957             if(pt == "after"){
54958                 newIndex++;
54959             }
54960             if(oldIndex < newIndex){
54961                 newIndex--;
54962             }
54963             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
54964                 return false;
54965             }
54966             cm.setLocked(oldIndex, locked, true);
54967             cm.moveColumn(oldIndex, newIndex);
54968             this.grid.fireEvent("columnmove", oldIndex, newIndex);
54969             return true;
54970         }
54971         return false;
54972     }
54973 });
54974 /*
54975  * Based on:
54976  * Ext JS Library 1.1.1
54977  * Copyright(c) 2006-2007, Ext JS, LLC.
54978  *
54979  * Originally Released Under LGPL - original licence link has changed is not relivant.
54980  *
54981  * Fork - LGPL
54982  * <script type="text/javascript">
54983  */
54984   
54985 /**
54986  * @class Roo.grid.GridView
54987  * @extends Roo.util.Observable
54988  *
54989  * @constructor
54990  * @param {Object} config
54991  */
54992 Roo.grid.GridView = function(config){
54993     Roo.grid.GridView.superclass.constructor.call(this);
54994     this.el = null;
54995
54996     Roo.apply(this, config);
54997 };
54998
54999 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55000
55001     unselectable :  'unselectable="on"',
55002     unselectableCls :  'x-unselectable',
55003     
55004     
55005     rowClass : "x-grid-row",
55006
55007     cellClass : "x-grid-col",
55008
55009     tdClass : "x-grid-td",
55010
55011     hdClass : "x-grid-hd",
55012
55013     splitClass : "x-grid-split",
55014
55015     sortClasses : ["sort-asc", "sort-desc"],
55016
55017     enableMoveAnim : false,
55018
55019     hlColor: "C3DAF9",
55020
55021     dh : Roo.DomHelper,
55022
55023     fly : Roo.Element.fly,
55024
55025     css : Roo.util.CSS,
55026
55027     borderWidth: 1,
55028
55029     splitOffset: 3,
55030
55031     scrollIncrement : 22,
55032
55033     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55034
55035     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55036
55037     bind : function(ds, cm){
55038         if(this.ds){
55039             this.ds.un("load", this.onLoad, this);
55040             this.ds.un("datachanged", this.onDataChange, this);
55041             this.ds.un("add", this.onAdd, this);
55042             this.ds.un("remove", this.onRemove, this);
55043             this.ds.un("update", this.onUpdate, this);
55044             this.ds.un("clear", this.onClear, this);
55045         }
55046         if(ds){
55047             ds.on("load", this.onLoad, this);
55048             ds.on("datachanged", this.onDataChange, this);
55049             ds.on("add", this.onAdd, this);
55050             ds.on("remove", this.onRemove, this);
55051             ds.on("update", this.onUpdate, this);
55052             ds.on("clear", this.onClear, this);
55053         }
55054         this.ds = ds;
55055
55056         if(this.cm){
55057             this.cm.un("widthchange", this.onColWidthChange, this);
55058             this.cm.un("headerchange", this.onHeaderChange, this);
55059             this.cm.un("hiddenchange", this.onHiddenChange, this);
55060             this.cm.un("columnmoved", this.onColumnMove, this);
55061             this.cm.un("columnlockchange", this.onColumnLock, this);
55062         }
55063         if(cm){
55064             this.generateRules(cm);
55065             cm.on("widthchange", this.onColWidthChange, this);
55066             cm.on("headerchange", this.onHeaderChange, this);
55067             cm.on("hiddenchange", this.onHiddenChange, this);
55068             cm.on("columnmoved", this.onColumnMove, this);
55069             cm.on("columnlockchange", this.onColumnLock, this);
55070         }
55071         this.cm = cm;
55072     },
55073
55074     init: function(grid){
55075         Roo.grid.GridView.superclass.init.call(this, grid);
55076
55077         this.bind(grid.dataSource, grid.colModel);
55078
55079         grid.on("headerclick", this.handleHeaderClick, this);
55080
55081         if(grid.trackMouseOver){
55082             grid.on("mouseover", this.onRowOver, this);
55083             grid.on("mouseout", this.onRowOut, this);
55084         }
55085         grid.cancelTextSelection = function(){};
55086         this.gridId = grid.id;
55087
55088         var tpls = this.templates || {};
55089
55090         if(!tpls.master){
55091             tpls.master = new Roo.Template(
55092                '<div class="x-grid" hidefocus="true">',
55093                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55094                   '<div class="x-grid-topbar"></div>',
55095                   '<div class="x-grid-scroller"><div></div></div>',
55096                   '<div class="x-grid-locked">',
55097                       '<div class="x-grid-header">{lockedHeader}</div>',
55098                       '<div class="x-grid-body">{lockedBody}</div>',
55099                   "</div>",
55100                   '<div class="x-grid-viewport">',
55101                       '<div class="x-grid-header">{header}</div>',
55102                       '<div class="x-grid-body">{body}</div>',
55103                   "</div>",
55104                   '<div class="x-grid-bottombar"></div>',
55105                  
55106                   '<div class="x-grid-resize-proxy">&#160;</div>',
55107                "</div>"
55108             );
55109             tpls.master.disableformats = true;
55110         }
55111
55112         if(!tpls.header){
55113             tpls.header = new Roo.Template(
55114                '<table border="0" cellspacing="0" cellpadding="0">',
55115                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55116                "</table>{splits}"
55117             );
55118             tpls.header.disableformats = true;
55119         }
55120         tpls.header.compile();
55121
55122         if(!tpls.hcell){
55123             tpls.hcell = new Roo.Template(
55124                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55125                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55126                 "</div></td>"
55127              );
55128              tpls.hcell.disableFormats = true;
55129         }
55130         tpls.hcell.compile();
55131
55132         if(!tpls.hsplit){
55133             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55134                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55135             tpls.hsplit.disableFormats = true;
55136         }
55137         tpls.hsplit.compile();
55138
55139         if(!tpls.body){
55140             tpls.body = new Roo.Template(
55141                '<table border="0" cellspacing="0" cellpadding="0">',
55142                "<tbody>{rows}</tbody>",
55143                "</table>"
55144             );
55145             tpls.body.disableFormats = true;
55146         }
55147         tpls.body.compile();
55148
55149         if(!tpls.row){
55150             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55151             tpls.row.disableFormats = true;
55152         }
55153         tpls.row.compile();
55154
55155         if(!tpls.cell){
55156             tpls.cell = new Roo.Template(
55157                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55158                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55159                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55160                 "</td>"
55161             );
55162             tpls.cell.disableFormats = true;
55163         }
55164         tpls.cell.compile();
55165
55166         this.templates = tpls;
55167     },
55168
55169     // remap these for backwards compat
55170     onColWidthChange : function(){
55171         this.updateColumns.apply(this, arguments);
55172     },
55173     onHeaderChange : function(){
55174         this.updateHeaders.apply(this, arguments);
55175     }, 
55176     onHiddenChange : function(){
55177         this.handleHiddenChange.apply(this, arguments);
55178     },
55179     onColumnMove : function(){
55180         this.handleColumnMove.apply(this, arguments);
55181     },
55182     onColumnLock : function(){
55183         this.handleLockChange.apply(this, arguments);
55184     },
55185
55186     onDataChange : function(){
55187         this.refresh();
55188         this.updateHeaderSortState();
55189     },
55190
55191     onClear : function(){
55192         this.refresh();
55193     },
55194
55195     onUpdate : function(ds, record){
55196         this.refreshRow(record);
55197     },
55198
55199     refreshRow : function(record){
55200         var ds = this.ds, index;
55201         if(typeof record == 'number'){
55202             index = record;
55203             record = ds.getAt(index);
55204         }else{
55205             index = ds.indexOf(record);
55206         }
55207         this.insertRows(ds, index, index, true);
55208         this.onRemove(ds, record, index+1, true);
55209         this.syncRowHeights(index, index);
55210         this.layout();
55211         this.fireEvent("rowupdated", this, index, record);
55212     },
55213
55214     onAdd : function(ds, records, index){
55215         this.insertRows(ds, index, index + (records.length-1));
55216     },
55217
55218     onRemove : function(ds, record, index, isUpdate){
55219         if(isUpdate !== true){
55220             this.fireEvent("beforerowremoved", this, index, record);
55221         }
55222         var bt = this.getBodyTable(), lt = this.getLockedTable();
55223         if(bt.rows[index]){
55224             bt.firstChild.removeChild(bt.rows[index]);
55225         }
55226         if(lt.rows[index]){
55227             lt.firstChild.removeChild(lt.rows[index]);
55228         }
55229         if(isUpdate !== true){
55230             this.stripeRows(index);
55231             this.syncRowHeights(index, index);
55232             this.layout();
55233             this.fireEvent("rowremoved", this, index, record);
55234         }
55235     },
55236
55237     onLoad : function(){
55238         this.scrollToTop();
55239     },
55240
55241     /**
55242      * Scrolls the grid to the top
55243      */
55244     scrollToTop : function(){
55245         if(this.scroller){
55246             this.scroller.dom.scrollTop = 0;
55247             this.syncScroll();
55248         }
55249     },
55250
55251     /**
55252      * Gets a panel in the header of the grid that can be used for toolbars etc.
55253      * After modifying the contents of this panel a call to grid.autoSize() may be
55254      * required to register any changes in size.
55255      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55256      * @return Roo.Element
55257      */
55258     getHeaderPanel : function(doShow){
55259         if(doShow){
55260             this.headerPanel.show();
55261         }
55262         return this.headerPanel;
55263     },
55264
55265     /**
55266      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55267      * After modifying the contents of this panel a call to grid.autoSize() may be
55268      * required to register any changes in size.
55269      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55270      * @return Roo.Element
55271      */
55272     getFooterPanel : function(doShow){
55273         if(doShow){
55274             this.footerPanel.show();
55275         }
55276         return this.footerPanel;
55277     },
55278
55279     initElements : function(){
55280         var E = Roo.Element;
55281         var el = this.grid.getGridEl().dom.firstChild;
55282         var cs = el.childNodes;
55283
55284         this.el = new E(el);
55285         
55286          this.focusEl = new E(el.firstChild);
55287         this.focusEl.swallowEvent("click", true);
55288         
55289         this.headerPanel = new E(cs[1]);
55290         this.headerPanel.enableDisplayMode("block");
55291
55292         this.scroller = new E(cs[2]);
55293         this.scrollSizer = new E(this.scroller.dom.firstChild);
55294
55295         this.lockedWrap = new E(cs[3]);
55296         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55297         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55298
55299         this.mainWrap = new E(cs[4]);
55300         this.mainHd = new E(this.mainWrap.dom.firstChild);
55301         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55302
55303         this.footerPanel = new E(cs[5]);
55304         this.footerPanel.enableDisplayMode("block");
55305
55306         this.resizeProxy = new E(cs[6]);
55307
55308         this.headerSelector = String.format(
55309            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55310            this.lockedHd.id, this.mainHd.id
55311         );
55312
55313         this.splitterSelector = String.format(
55314            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55315            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55316         );
55317     },
55318     idToCssName : function(s)
55319     {
55320         return s.replace(/[^a-z0-9]+/ig, '-');
55321     },
55322
55323     getHeaderCell : function(index){
55324         return Roo.DomQuery.select(this.headerSelector)[index];
55325     },
55326
55327     getHeaderCellMeasure : function(index){
55328         return this.getHeaderCell(index).firstChild;
55329     },
55330
55331     getHeaderCellText : function(index){
55332         return this.getHeaderCell(index).firstChild.firstChild;
55333     },
55334
55335     getLockedTable : function(){
55336         return this.lockedBody.dom.firstChild;
55337     },
55338
55339     getBodyTable : function(){
55340         return this.mainBody.dom.firstChild;
55341     },
55342
55343     getLockedRow : function(index){
55344         return this.getLockedTable().rows[index];
55345     },
55346
55347     getRow : function(index){
55348         return this.getBodyTable().rows[index];
55349     },
55350
55351     getRowComposite : function(index){
55352         if(!this.rowEl){
55353             this.rowEl = new Roo.CompositeElementLite();
55354         }
55355         var els = [], lrow, mrow;
55356         if(lrow = this.getLockedRow(index)){
55357             els.push(lrow);
55358         }
55359         if(mrow = this.getRow(index)){
55360             els.push(mrow);
55361         }
55362         this.rowEl.elements = els;
55363         return this.rowEl;
55364     },
55365     /**
55366      * Gets the 'td' of the cell
55367      * 
55368      * @param {Integer} rowIndex row to select
55369      * @param {Integer} colIndex column to select
55370      * 
55371      * @return {Object} 
55372      */
55373     getCell : function(rowIndex, colIndex){
55374         var locked = this.cm.getLockedCount();
55375         var source;
55376         if(colIndex < locked){
55377             source = this.lockedBody.dom.firstChild;
55378         }else{
55379             source = this.mainBody.dom.firstChild;
55380             colIndex -= locked;
55381         }
55382         return source.rows[rowIndex].childNodes[colIndex];
55383     },
55384
55385     getCellText : function(rowIndex, colIndex){
55386         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55387     },
55388
55389     getCellBox : function(cell){
55390         var b = this.fly(cell).getBox();
55391         if(Roo.isOpera){ // opera fails to report the Y
55392             b.y = cell.offsetTop + this.mainBody.getY();
55393         }
55394         return b;
55395     },
55396
55397     getCellIndex : function(cell){
55398         var id = String(cell.className).match(this.cellRE);
55399         if(id){
55400             return parseInt(id[1], 10);
55401         }
55402         return 0;
55403     },
55404
55405     findHeaderIndex : function(n){
55406         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55407         return r ? this.getCellIndex(r) : false;
55408     },
55409
55410     findHeaderCell : function(n){
55411         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55412         return r ? r : false;
55413     },
55414
55415     findRowIndex : function(n){
55416         if(!n){
55417             return false;
55418         }
55419         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55420         return r ? r.rowIndex : false;
55421     },
55422
55423     findCellIndex : function(node){
55424         var stop = this.el.dom;
55425         while(node && node != stop){
55426             if(this.findRE.test(node.className)){
55427                 return this.getCellIndex(node);
55428             }
55429             node = node.parentNode;
55430         }
55431         return false;
55432     },
55433
55434     getColumnId : function(index){
55435         return this.cm.getColumnId(index);
55436     },
55437
55438     getSplitters : function()
55439     {
55440         if(this.splitterSelector){
55441            return Roo.DomQuery.select(this.splitterSelector);
55442         }else{
55443             return null;
55444       }
55445     },
55446
55447     getSplitter : function(index){
55448         return this.getSplitters()[index];
55449     },
55450
55451     onRowOver : function(e, t){
55452         var row;
55453         if((row = this.findRowIndex(t)) !== false){
55454             this.getRowComposite(row).addClass("x-grid-row-over");
55455         }
55456     },
55457
55458     onRowOut : function(e, t){
55459         var row;
55460         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55461             this.getRowComposite(row).removeClass("x-grid-row-over");
55462         }
55463     },
55464
55465     renderHeaders : function(){
55466         var cm = this.cm;
55467         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55468         var cb = [], lb = [], sb = [], lsb = [], p = {};
55469         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55470             p.cellId = "x-grid-hd-0-" + i;
55471             p.splitId = "x-grid-csplit-0-" + i;
55472             p.id = cm.getColumnId(i);
55473             p.value = cm.getColumnHeader(i) || "";
55474             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55475             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55476             if(!cm.isLocked(i)){
55477                 cb[cb.length] = ct.apply(p);
55478                 sb[sb.length] = st.apply(p);
55479             }else{
55480                 lb[lb.length] = ct.apply(p);
55481                 lsb[lsb.length] = st.apply(p);
55482             }
55483         }
55484         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55485                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55486     },
55487
55488     updateHeaders : function(){
55489         var html = this.renderHeaders();
55490         this.lockedHd.update(html[0]);
55491         this.mainHd.update(html[1]);
55492     },
55493
55494     /**
55495      * Focuses the specified row.
55496      * @param {Number} row The row index
55497      */
55498     focusRow : function(row)
55499     {
55500         //Roo.log('GridView.focusRow');
55501         var x = this.scroller.dom.scrollLeft;
55502         this.focusCell(row, 0, false);
55503         this.scroller.dom.scrollLeft = x;
55504     },
55505
55506     /**
55507      * Focuses the specified cell.
55508      * @param {Number} row The row index
55509      * @param {Number} col The column index
55510      * @param {Boolean} hscroll false to disable horizontal scrolling
55511      */
55512     focusCell : function(row, col, hscroll)
55513     {
55514         //Roo.log('GridView.focusCell');
55515         var el = this.ensureVisible(row, col, hscroll);
55516         this.focusEl.alignTo(el, "tl-tl");
55517         if(Roo.isGecko){
55518             this.focusEl.focus();
55519         }else{
55520             this.focusEl.focus.defer(1, this.focusEl);
55521         }
55522     },
55523
55524     /**
55525      * Scrolls the specified cell into view
55526      * @param {Number} row The row index
55527      * @param {Number} col The column index
55528      * @param {Boolean} hscroll false to disable horizontal scrolling
55529      */
55530     ensureVisible : function(row, col, hscroll)
55531     {
55532         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55533         //return null; //disable for testing.
55534         if(typeof row != "number"){
55535             row = row.rowIndex;
55536         }
55537         if(row < 0 && row >= this.ds.getCount()){
55538             return  null;
55539         }
55540         col = (col !== undefined ? col : 0);
55541         var cm = this.grid.colModel;
55542         while(cm.isHidden(col)){
55543             col++;
55544         }
55545
55546         var el = this.getCell(row, col);
55547         if(!el){
55548             return null;
55549         }
55550         var c = this.scroller.dom;
55551
55552         var ctop = parseInt(el.offsetTop, 10);
55553         var cleft = parseInt(el.offsetLeft, 10);
55554         var cbot = ctop + el.offsetHeight;
55555         var cright = cleft + el.offsetWidth;
55556         
55557         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55558         var stop = parseInt(c.scrollTop, 10);
55559         var sleft = parseInt(c.scrollLeft, 10);
55560         var sbot = stop + ch;
55561         var sright = sleft + c.clientWidth;
55562         /*
55563         Roo.log('GridView.ensureVisible:' +
55564                 ' ctop:' + ctop +
55565                 ' c.clientHeight:' + c.clientHeight +
55566                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55567                 ' stop:' + stop +
55568                 ' cbot:' + cbot +
55569                 ' sbot:' + sbot +
55570                 ' ch:' + ch  
55571                 );
55572         */
55573         if(ctop < stop){
55574              c.scrollTop = ctop;
55575             //Roo.log("set scrolltop to ctop DISABLE?");
55576         }else if(cbot > sbot){
55577             //Roo.log("set scrolltop to cbot-ch");
55578             c.scrollTop = cbot-ch;
55579         }
55580         
55581         if(hscroll !== false){
55582             if(cleft < sleft){
55583                 c.scrollLeft = cleft;
55584             }else if(cright > sright){
55585                 c.scrollLeft = cright-c.clientWidth;
55586             }
55587         }
55588          
55589         return el;
55590     },
55591
55592     updateColumns : function(){
55593         this.grid.stopEditing();
55594         var cm = this.grid.colModel, colIds = this.getColumnIds();
55595         //var totalWidth = cm.getTotalWidth();
55596         var pos = 0;
55597         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55598             //if(cm.isHidden(i)) continue;
55599             var w = cm.getColumnWidth(i);
55600             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55601             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55602         }
55603         this.updateSplitters();
55604     },
55605
55606     generateRules : function(cm){
55607         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55608         Roo.util.CSS.removeStyleSheet(rulesId);
55609         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55610             var cid = cm.getColumnId(i);
55611             var align = '';
55612             if(cm.config[i].align){
55613                 align = 'text-align:'+cm.config[i].align+';';
55614             }
55615             var hidden = '';
55616             if(cm.isHidden(i)){
55617                 hidden = 'display:none;';
55618             }
55619             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55620             ruleBuf.push(
55621                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55622                     this.hdSelector, cid, " {\n", align, width, "}\n",
55623                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55624                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55625         }
55626         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55627     },
55628
55629     updateSplitters : function(){
55630         var cm = this.cm, s = this.getSplitters();
55631         if(s){ // splitters not created yet
55632             var pos = 0, locked = true;
55633             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55634                 if(cm.isHidden(i)) {
55635                     continue;
55636                 }
55637                 var w = cm.getColumnWidth(i); // make sure it's a number
55638                 if(!cm.isLocked(i) && locked){
55639                     pos = 0;
55640                     locked = false;
55641                 }
55642                 pos += w;
55643                 s[i].style.left = (pos-this.splitOffset) + "px";
55644             }
55645         }
55646     },
55647
55648     handleHiddenChange : function(colModel, colIndex, hidden){
55649         if(hidden){
55650             this.hideColumn(colIndex);
55651         }else{
55652             this.unhideColumn(colIndex);
55653         }
55654     },
55655
55656     hideColumn : function(colIndex){
55657         var cid = this.getColumnId(colIndex);
55658         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55659         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55660         if(Roo.isSafari){
55661             this.updateHeaders();
55662         }
55663         this.updateSplitters();
55664         this.layout();
55665     },
55666
55667     unhideColumn : function(colIndex){
55668         var cid = this.getColumnId(colIndex);
55669         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55670         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55671
55672         if(Roo.isSafari){
55673             this.updateHeaders();
55674         }
55675         this.updateSplitters();
55676         this.layout();
55677     },
55678
55679     insertRows : function(dm, firstRow, lastRow, isUpdate){
55680         if(firstRow == 0 && lastRow == dm.getCount()-1){
55681             this.refresh();
55682         }else{
55683             if(!isUpdate){
55684                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55685             }
55686             var s = this.getScrollState();
55687             var markup = this.renderRows(firstRow, lastRow);
55688             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55689             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55690             this.restoreScroll(s);
55691             if(!isUpdate){
55692                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55693                 this.syncRowHeights(firstRow, lastRow);
55694                 this.stripeRows(firstRow);
55695                 this.layout();
55696             }
55697         }
55698     },
55699
55700     bufferRows : function(markup, target, index){
55701         var before = null, trows = target.rows, tbody = target.tBodies[0];
55702         if(index < trows.length){
55703             before = trows[index];
55704         }
55705         var b = document.createElement("div");
55706         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55707         var rows = b.firstChild.rows;
55708         for(var i = 0, len = rows.length; i < len; i++){
55709             if(before){
55710                 tbody.insertBefore(rows[0], before);
55711             }else{
55712                 tbody.appendChild(rows[0]);
55713             }
55714         }
55715         b.innerHTML = "";
55716         b = null;
55717     },
55718
55719     deleteRows : function(dm, firstRow, lastRow){
55720         if(dm.getRowCount()<1){
55721             this.fireEvent("beforerefresh", this);
55722             this.mainBody.update("");
55723             this.lockedBody.update("");
55724             this.fireEvent("refresh", this);
55725         }else{
55726             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55727             var bt = this.getBodyTable();
55728             var tbody = bt.firstChild;
55729             var rows = bt.rows;
55730             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55731                 tbody.removeChild(rows[firstRow]);
55732             }
55733             this.stripeRows(firstRow);
55734             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55735         }
55736     },
55737
55738     updateRows : function(dataSource, firstRow, lastRow){
55739         var s = this.getScrollState();
55740         this.refresh();
55741         this.restoreScroll(s);
55742     },
55743
55744     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55745         if(!noRefresh){
55746            this.refresh();
55747         }
55748         this.updateHeaderSortState();
55749     },
55750
55751     getScrollState : function(){
55752         
55753         var sb = this.scroller.dom;
55754         return {left: sb.scrollLeft, top: sb.scrollTop};
55755     },
55756
55757     stripeRows : function(startRow){
55758         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55759             return;
55760         }
55761         startRow = startRow || 0;
55762         var rows = this.getBodyTable().rows;
55763         var lrows = this.getLockedTable().rows;
55764         var cls = ' x-grid-row-alt ';
55765         for(var i = startRow, len = rows.length; i < len; i++){
55766             var row = rows[i], lrow = lrows[i];
55767             var isAlt = ((i+1) % 2 == 0);
55768             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55769             if(isAlt == hasAlt){
55770                 continue;
55771             }
55772             if(isAlt){
55773                 row.className += " x-grid-row-alt";
55774             }else{
55775                 row.className = row.className.replace("x-grid-row-alt", "");
55776             }
55777             if(lrow){
55778                 lrow.className = row.className;
55779             }
55780         }
55781     },
55782
55783     restoreScroll : function(state){
55784         //Roo.log('GridView.restoreScroll');
55785         var sb = this.scroller.dom;
55786         sb.scrollLeft = state.left;
55787         sb.scrollTop = state.top;
55788         this.syncScroll();
55789     },
55790
55791     syncScroll : function(){
55792         //Roo.log('GridView.syncScroll');
55793         var sb = this.scroller.dom;
55794         var sh = this.mainHd.dom;
55795         var bs = this.mainBody.dom;
55796         var lv = this.lockedBody.dom;
55797         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55798         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55799     },
55800
55801     handleScroll : function(e){
55802         this.syncScroll();
55803         var sb = this.scroller.dom;
55804         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55805         e.stopEvent();
55806     },
55807
55808     handleWheel : function(e){
55809         var d = e.getWheelDelta();
55810         this.scroller.dom.scrollTop -= d*22;
55811         // set this here to prevent jumpy scrolling on large tables
55812         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55813         e.stopEvent();
55814     },
55815
55816     renderRows : function(startRow, endRow){
55817         // pull in all the crap needed to render rows
55818         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55819         var colCount = cm.getColumnCount();
55820
55821         if(ds.getCount() < 1){
55822             return ["", ""];
55823         }
55824
55825         // build a map for all the columns
55826         var cs = [];
55827         for(var i = 0; i < colCount; i++){
55828             var name = cm.getDataIndex(i);
55829             cs[i] = {
55830                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55831                 renderer : cm.getRenderer(i),
55832                 id : cm.getColumnId(i),
55833                 locked : cm.isLocked(i),
55834                 has_editor : cm.isCellEditable(i)
55835             };
55836         }
55837
55838         startRow = startRow || 0;
55839         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55840
55841         // records to render
55842         var rs = ds.getRange(startRow, endRow);
55843
55844         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55845     },
55846
55847     // As much as I hate to duplicate code, this was branched because FireFox really hates
55848     // [].join("") on strings. The performance difference was substantial enough to
55849     // branch this function
55850     doRender : Roo.isGecko ?
55851             function(cs, rs, ds, startRow, colCount, stripe){
55852                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55853                 // buffers
55854                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55855                 
55856                 var hasListener = this.grid.hasListener('rowclass');
55857                 var rowcfg = {};
55858                 for(var j = 0, len = rs.length; j < len; j++){
55859                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55860                     for(var i = 0; i < colCount; i++){
55861                         c = cs[i];
55862                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55863                         p.id = c.id;
55864                         p.css = p.attr = "";
55865                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55866                         if(p.value == undefined || p.value === "") {
55867                             p.value = "&#160;";
55868                         }
55869                         if(c.has_editor){
55870                             p.css += ' x-grid-editable-cell';
55871                         }
55872                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55873                             p.css +=  ' x-grid-dirty-cell';
55874                         }
55875                         var markup = ct.apply(p);
55876                         if(!c.locked){
55877                             cb+= markup;
55878                         }else{
55879                             lcb+= markup;
55880                         }
55881                     }
55882                     var alt = [];
55883                     if(stripe && ((rowIndex+1) % 2 == 0)){
55884                         alt.push("x-grid-row-alt")
55885                     }
55886                     if(r.dirty){
55887                         alt.push(  " x-grid-dirty-row");
55888                     }
55889                     rp.cells = lcb;
55890                     if(this.getRowClass){
55891                         alt.push(this.getRowClass(r, rowIndex));
55892                     }
55893                     if (hasListener) {
55894                         rowcfg = {
55895                              
55896                             record: r,
55897                             rowIndex : rowIndex,
55898                             rowClass : ''
55899                         };
55900                         this.grid.fireEvent('rowclass', this, rowcfg);
55901                         alt.push(rowcfg.rowClass);
55902                     }
55903                     rp.alt = alt.join(" ");
55904                     lbuf+= rt.apply(rp);
55905                     rp.cells = cb;
55906                     buf+=  rt.apply(rp);
55907                 }
55908                 return [lbuf, buf];
55909             } :
55910             function(cs, rs, ds, startRow, colCount, stripe){
55911                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55912                 // buffers
55913                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55914                 var hasListener = this.grid.hasListener('rowclass');
55915  
55916                 var rowcfg = {};
55917                 for(var j = 0, len = rs.length; j < len; j++){
55918                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55919                     for(var i = 0; i < colCount; i++){
55920                         c = cs[i];
55921                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55922                         p.id = c.id;
55923                         p.css = p.attr = "";
55924                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55925                         if(p.value == undefined || p.value === "") {
55926                             p.value = "&#160;";
55927                         }
55928                         //Roo.log(c);
55929                          if(c.has_editor){
55930                             p.css += ' x-grid-editable-cell';
55931                         }
55932                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55933                             p.css += ' x-grid-dirty-cell' 
55934                         }
55935                         
55936                         var markup = ct.apply(p);
55937                         if(!c.locked){
55938                             cb[cb.length] = markup;
55939                         }else{
55940                             lcb[lcb.length] = markup;
55941                         }
55942                     }
55943                     var alt = [];
55944                     if(stripe && ((rowIndex+1) % 2 == 0)){
55945                         alt.push( "x-grid-row-alt");
55946                     }
55947                     if(r.dirty){
55948                         alt.push(" x-grid-dirty-row");
55949                     }
55950                     rp.cells = lcb;
55951                     if(this.getRowClass){
55952                         alt.push( this.getRowClass(r, rowIndex));
55953                     }
55954                     if (hasListener) {
55955                         rowcfg = {
55956                              
55957                             record: r,
55958                             rowIndex : rowIndex,
55959                             rowClass : ''
55960                         };
55961                         this.grid.fireEvent('rowclass', this, rowcfg);
55962                         alt.push(rowcfg.rowClass);
55963                     }
55964                     
55965                     rp.alt = alt.join(" ");
55966                     rp.cells = lcb.join("");
55967                     lbuf[lbuf.length] = rt.apply(rp);
55968                     rp.cells = cb.join("");
55969                     buf[buf.length] =  rt.apply(rp);
55970                 }
55971                 return [lbuf.join(""), buf.join("")];
55972             },
55973
55974     renderBody : function(){
55975         var markup = this.renderRows();
55976         var bt = this.templates.body;
55977         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
55978     },
55979
55980     /**
55981      * Refreshes the grid
55982      * @param {Boolean} headersToo
55983      */
55984     refresh : function(headersToo){
55985         this.fireEvent("beforerefresh", this);
55986         this.grid.stopEditing();
55987         var result = this.renderBody();
55988         this.lockedBody.update(result[0]);
55989         this.mainBody.update(result[1]);
55990         if(headersToo === true){
55991             this.updateHeaders();
55992             this.updateColumns();
55993             this.updateSplitters();
55994             this.updateHeaderSortState();
55995         }
55996         this.syncRowHeights();
55997         this.layout();
55998         this.fireEvent("refresh", this);
55999     },
56000
56001     handleColumnMove : function(cm, oldIndex, newIndex){
56002         this.indexMap = null;
56003         var s = this.getScrollState();
56004         this.refresh(true);
56005         this.restoreScroll(s);
56006         this.afterMove(newIndex);
56007     },
56008
56009     afterMove : function(colIndex){
56010         if(this.enableMoveAnim && Roo.enableFx){
56011             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56012         }
56013         // if multisort - fix sortOrder, and reload..
56014         if (this.grid.dataSource.multiSort) {
56015             // the we can call sort again..
56016             var dm = this.grid.dataSource;
56017             var cm = this.grid.colModel;
56018             var so = [];
56019             for(var i = 0; i < cm.config.length; i++ ) {
56020                 
56021                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56022                     continue; // dont' bother, it's not in sort list or being set.
56023                 }
56024                 
56025                 so.push(cm.config[i].dataIndex);
56026             };
56027             dm.sortOrder = so;
56028             dm.load(dm.lastOptions);
56029             
56030             
56031         }
56032         
56033     },
56034
56035     updateCell : function(dm, rowIndex, dataIndex){
56036         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56037         if(typeof colIndex == "undefined"){ // not present in grid
56038             return;
56039         }
56040         var cm = this.grid.colModel;
56041         var cell = this.getCell(rowIndex, colIndex);
56042         var cellText = this.getCellText(rowIndex, colIndex);
56043
56044         var p = {
56045             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56046             id : cm.getColumnId(colIndex),
56047             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56048         };
56049         var renderer = cm.getRenderer(colIndex);
56050         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56051         if(typeof val == "undefined" || val === "") {
56052             val = "&#160;";
56053         }
56054         cellText.innerHTML = val;
56055         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56056         this.syncRowHeights(rowIndex, rowIndex);
56057     },
56058
56059     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56060         var maxWidth = 0;
56061         if(this.grid.autoSizeHeaders){
56062             var h = this.getHeaderCellMeasure(colIndex);
56063             maxWidth = Math.max(maxWidth, h.scrollWidth);
56064         }
56065         var tb, index;
56066         if(this.cm.isLocked(colIndex)){
56067             tb = this.getLockedTable();
56068             index = colIndex;
56069         }else{
56070             tb = this.getBodyTable();
56071             index = colIndex - this.cm.getLockedCount();
56072         }
56073         if(tb && tb.rows){
56074             var rows = tb.rows;
56075             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56076             for(var i = 0; i < stopIndex; i++){
56077                 var cell = rows[i].childNodes[index].firstChild;
56078                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56079             }
56080         }
56081         return maxWidth + /*margin for error in IE*/ 5;
56082     },
56083     /**
56084      * Autofit a column to its content.
56085      * @param {Number} colIndex
56086      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56087      */
56088      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56089          if(this.cm.isHidden(colIndex)){
56090              return; // can't calc a hidden column
56091          }
56092         if(forceMinSize){
56093             var cid = this.cm.getColumnId(colIndex);
56094             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56095            if(this.grid.autoSizeHeaders){
56096                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56097            }
56098         }
56099         var newWidth = this.calcColumnWidth(colIndex);
56100         this.cm.setColumnWidth(colIndex,
56101             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56102         if(!suppressEvent){
56103             this.grid.fireEvent("columnresize", colIndex, newWidth);
56104         }
56105     },
56106
56107     /**
56108      * Autofits all columns to their content and then expands to fit any extra space in the grid
56109      */
56110      autoSizeColumns : function(){
56111         var cm = this.grid.colModel;
56112         var colCount = cm.getColumnCount();
56113         for(var i = 0; i < colCount; i++){
56114             this.autoSizeColumn(i, true, true);
56115         }
56116         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56117             this.fitColumns();
56118         }else{
56119             this.updateColumns();
56120             this.layout();
56121         }
56122     },
56123
56124     /**
56125      * Autofits all columns to the grid's width proportionate with their current size
56126      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56127      */
56128     fitColumns : function(reserveScrollSpace){
56129         var cm = this.grid.colModel;
56130         var colCount = cm.getColumnCount();
56131         var cols = [];
56132         var width = 0;
56133         var i, w;
56134         for (i = 0; i < colCount; i++){
56135             if(!cm.isHidden(i) && !cm.isFixed(i)){
56136                 w = cm.getColumnWidth(i);
56137                 cols.push(i);
56138                 cols.push(w);
56139                 width += w;
56140             }
56141         }
56142         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56143         if(reserveScrollSpace){
56144             avail -= 17;
56145         }
56146         var frac = (avail - cm.getTotalWidth())/width;
56147         while (cols.length){
56148             w = cols.pop();
56149             i = cols.pop();
56150             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56151         }
56152         this.updateColumns();
56153         this.layout();
56154     },
56155
56156     onRowSelect : function(rowIndex){
56157         var row = this.getRowComposite(rowIndex);
56158         row.addClass("x-grid-row-selected");
56159     },
56160
56161     onRowDeselect : function(rowIndex){
56162         var row = this.getRowComposite(rowIndex);
56163         row.removeClass("x-grid-row-selected");
56164     },
56165
56166     onCellSelect : function(row, col){
56167         var cell = this.getCell(row, col);
56168         if(cell){
56169             Roo.fly(cell).addClass("x-grid-cell-selected");
56170         }
56171     },
56172
56173     onCellDeselect : function(row, col){
56174         var cell = this.getCell(row, col);
56175         if(cell){
56176             Roo.fly(cell).removeClass("x-grid-cell-selected");
56177         }
56178     },
56179
56180     updateHeaderSortState : function(){
56181         
56182         // sort state can be single { field: xxx, direction : yyy}
56183         // or   { xxx=>ASC , yyy : DESC ..... }
56184         
56185         var mstate = {};
56186         if (!this.ds.multiSort) { 
56187             var state = this.ds.getSortState();
56188             if(!state){
56189                 return;
56190             }
56191             mstate[state.field] = state.direction;
56192             // FIXME... - this is not used here.. but might be elsewhere..
56193             this.sortState = state;
56194             
56195         } else {
56196             mstate = this.ds.sortToggle;
56197         }
56198         //remove existing sort classes..
56199         
56200         var sc = this.sortClasses;
56201         var hds = this.el.select(this.headerSelector).removeClass(sc);
56202         
56203         for(var f in mstate) {
56204         
56205             var sortColumn = this.cm.findColumnIndex(f);
56206             
56207             if(sortColumn != -1){
56208                 var sortDir = mstate[f];        
56209                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56210             }
56211         }
56212         
56213          
56214         
56215     },
56216
56217
56218     handleHeaderClick : function(g, index,e){
56219         
56220         Roo.log("header click");
56221         
56222         if (Roo.isTouch) {
56223             // touch events on header are handled by context
56224             this.handleHdCtx(g,index,e);
56225             return;
56226         }
56227         
56228         
56229         if(this.headersDisabled){
56230             return;
56231         }
56232         var dm = g.dataSource, cm = g.colModel;
56233         if(!cm.isSortable(index)){
56234             return;
56235         }
56236         g.stopEditing();
56237         
56238         if (dm.multiSort) {
56239             // update the sortOrder
56240             var so = [];
56241             for(var i = 0; i < cm.config.length; i++ ) {
56242                 
56243                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56244                     continue; // dont' bother, it's not in sort list or being set.
56245                 }
56246                 
56247                 so.push(cm.config[i].dataIndex);
56248             };
56249             dm.sortOrder = so;
56250         }
56251         
56252         
56253         dm.sort(cm.getDataIndex(index));
56254     },
56255
56256
56257     destroy : function(){
56258         if(this.colMenu){
56259             this.colMenu.removeAll();
56260             Roo.menu.MenuMgr.unregister(this.colMenu);
56261             this.colMenu.getEl().remove();
56262             delete this.colMenu;
56263         }
56264         if(this.hmenu){
56265             this.hmenu.removeAll();
56266             Roo.menu.MenuMgr.unregister(this.hmenu);
56267             this.hmenu.getEl().remove();
56268             delete this.hmenu;
56269         }
56270         if(this.grid.enableColumnMove){
56271             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56272             if(dds){
56273                 for(var dd in dds){
56274                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56275                         var elid = dds[dd].dragElId;
56276                         dds[dd].unreg();
56277                         Roo.get(elid).remove();
56278                     } else if(dds[dd].config.isTarget){
56279                         dds[dd].proxyTop.remove();
56280                         dds[dd].proxyBottom.remove();
56281                         dds[dd].unreg();
56282                     }
56283                     if(Roo.dd.DDM.locationCache[dd]){
56284                         delete Roo.dd.DDM.locationCache[dd];
56285                     }
56286                 }
56287                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56288             }
56289         }
56290         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56291         this.bind(null, null);
56292         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56293     },
56294
56295     handleLockChange : function(){
56296         this.refresh(true);
56297     },
56298
56299     onDenyColumnLock : function(){
56300
56301     },
56302
56303     onDenyColumnHide : function(){
56304
56305     },
56306
56307     handleHdMenuClick : function(item){
56308         var index = this.hdCtxIndex;
56309         var cm = this.cm, ds = this.ds;
56310         switch(item.id){
56311             case "asc":
56312                 ds.sort(cm.getDataIndex(index), "ASC");
56313                 break;
56314             case "desc":
56315                 ds.sort(cm.getDataIndex(index), "DESC");
56316                 break;
56317             case "lock":
56318                 var lc = cm.getLockedCount();
56319                 if(cm.getColumnCount(true) <= lc+1){
56320                     this.onDenyColumnLock();
56321                     return;
56322                 }
56323                 if(lc != index){
56324                     cm.setLocked(index, true, true);
56325                     cm.moveColumn(index, lc);
56326                     this.grid.fireEvent("columnmove", index, lc);
56327                 }else{
56328                     cm.setLocked(index, true);
56329                 }
56330             break;
56331             case "unlock":
56332                 var lc = cm.getLockedCount();
56333                 if((lc-1) != index){
56334                     cm.setLocked(index, false, true);
56335                     cm.moveColumn(index, lc-1);
56336                     this.grid.fireEvent("columnmove", index, lc-1);
56337                 }else{
56338                     cm.setLocked(index, false);
56339                 }
56340             break;
56341             case 'wider': // used to expand cols on touch..
56342             case 'narrow':
56343                 var cw = cm.getColumnWidth(index);
56344                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56345                 cw = Math.max(0, cw);
56346                 cw = Math.min(cw,4000);
56347                 cm.setColumnWidth(index, cw);
56348                 break;
56349                 
56350             default:
56351                 index = cm.getIndexById(item.id.substr(4));
56352                 if(index != -1){
56353                     if(item.checked && cm.getColumnCount(true) <= 1){
56354                         this.onDenyColumnHide();
56355                         return false;
56356                     }
56357                     cm.setHidden(index, item.checked);
56358                 }
56359         }
56360         return true;
56361     },
56362
56363     beforeColMenuShow : function(){
56364         var cm = this.cm,  colCount = cm.getColumnCount();
56365         this.colMenu.removeAll();
56366         for(var i = 0; i < colCount; i++){
56367             this.colMenu.add(new Roo.menu.CheckItem({
56368                 id: "col-"+cm.getColumnId(i),
56369                 text: cm.getColumnHeader(i),
56370                 checked: !cm.isHidden(i),
56371                 hideOnClick:false
56372             }));
56373         }
56374     },
56375
56376     handleHdCtx : function(g, index, e){
56377         e.stopEvent();
56378         var hd = this.getHeaderCell(index);
56379         this.hdCtxIndex = index;
56380         var ms = this.hmenu.items, cm = this.cm;
56381         ms.get("asc").setDisabled(!cm.isSortable(index));
56382         ms.get("desc").setDisabled(!cm.isSortable(index));
56383         if(this.grid.enableColLock !== false){
56384             ms.get("lock").setDisabled(cm.isLocked(index));
56385             ms.get("unlock").setDisabled(!cm.isLocked(index));
56386         }
56387         this.hmenu.show(hd, "tl-bl");
56388     },
56389
56390     handleHdOver : function(e){
56391         var hd = this.findHeaderCell(e.getTarget());
56392         if(hd && !this.headersDisabled){
56393             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56394                this.fly(hd).addClass("x-grid-hd-over");
56395             }
56396         }
56397     },
56398
56399     handleHdOut : function(e){
56400         var hd = this.findHeaderCell(e.getTarget());
56401         if(hd){
56402             this.fly(hd).removeClass("x-grid-hd-over");
56403         }
56404     },
56405
56406     handleSplitDblClick : function(e, t){
56407         var i = this.getCellIndex(t);
56408         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56409             this.autoSizeColumn(i, true);
56410             this.layout();
56411         }
56412     },
56413
56414     render : function(){
56415
56416         var cm = this.cm;
56417         var colCount = cm.getColumnCount();
56418
56419         if(this.grid.monitorWindowResize === true){
56420             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56421         }
56422         var header = this.renderHeaders();
56423         var body = this.templates.body.apply({rows:""});
56424         var html = this.templates.master.apply({
56425             lockedBody: body,
56426             body: body,
56427             lockedHeader: header[0],
56428             header: header[1]
56429         });
56430
56431         //this.updateColumns();
56432
56433         this.grid.getGridEl().dom.innerHTML = html;
56434
56435         this.initElements();
56436         
56437         // a kludge to fix the random scolling effect in webkit
56438         this.el.on("scroll", function() {
56439             this.el.dom.scrollTop=0; // hopefully not recursive..
56440         },this);
56441
56442         this.scroller.on("scroll", this.handleScroll, this);
56443         this.lockedBody.on("mousewheel", this.handleWheel, this);
56444         this.mainBody.on("mousewheel", this.handleWheel, this);
56445
56446         this.mainHd.on("mouseover", this.handleHdOver, this);
56447         this.mainHd.on("mouseout", this.handleHdOut, this);
56448         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56449                 {delegate: "."+this.splitClass});
56450
56451         this.lockedHd.on("mouseover", this.handleHdOver, this);
56452         this.lockedHd.on("mouseout", this.handleHdOut, this);
56453         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56454                 {delegate: "."+this.splitClass});
56455
56456         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56457             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56458         }
56459
56460         this.updateSplitters();
56461
56462         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56463             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56464             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56465         }
56466
56467         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56468             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56469             this.hmenu.add(
56470                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56471                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56472             );
56473             if(this.grid.enableColLock !== false){
56474                 this.hmenu.add('-',
56475                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56476                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56477                 );
56478             }
56479             if (Roo.isTouch) {
56480                  this.hmenu.add('-',
56481                     {id:"wider", text: this.columnsWiderText},
56482                     {id:"narrow", text: this.columnsNarrowText }
56483                 );
56484                 
56485                  
56486             }
56487             
56488             if(this.grid.enableColumnHide !== false){
56489
56490                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56491                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56492                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56493
56494                 this.hmenu.add('-',
56495                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56496                 );
56497             }
56498             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56499
56500             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56501         }
56502
56503         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56504             this.dd = new Roo.grid.GridDragZone(this.grid, {
56505                 ddGroup : this.grid.ddGroup || 'GridDD'
56506             });
56507             
56508         }
56509
56510         /*
56511         for(var i = 0; i < colCount; i++){
56512             if(cm.isHidden(i)){
56513                 this.hideColumn(i);
56514             }
56515             if(cm.config[i].align){
56516                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56517                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56518             }
56519         }*/
56520         
56521         this.updateHeaderSortState();
56522
56523         this.beforeInitialResize();
56524         this.layout(true);
56525
56526         // two part rendering gives faster view to the user
56527         this.renderPhase2.defer(1, this);
56528     },
56529
56530     renderPhase2 : function(){
56531         // render the rows now
56532         this.refresh();
56533         if(this.grid.autoSizeColumns){
56534             this.autoSizeColumns();
56535         }
56536     },
56537
56538     beforeInitialResize : function(){
56539
56540     },
56541
56542     onColumnSplitterMoved : function(i, w){
56543         this.userResized = true;
56544         var cm = this.grid.colModel;
56545         cm.setColumnWidth(i, w, true);
56546         var cid = cm.getColumnId(i);
56547         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56548         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56549         this.updateSplitters();
56550         this.layout();
56551         this.grid.fireEvent("columnresize", i, w);
56552     },
56553
56554     syncRowHeights : function(startIndex, endIndex){
56555         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56556             startIndex = startIndex || 0;
56557             var mrows = this.getBodyTable().rows;
56558             var lrows = this.getLockedTable().rows;
56559             var len = mrows.length-1;
56560             endIndex = Math.min(endIndex || len, len);
56561             for(var i = startIndex; i <= endIndex; i++){
56562                 var m = mrows[i], l = lrows[i];
56563                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56564                 m.style.height = l.style.height = h + "px";
56565             }
56566         }
56567     },
56568
56569     layout : function(initialRender, is2ndPass){
56570         var g = this.grid;
56571         var auto = g.autoHeight;
56572         var scrollOffset = 16;
56573         var c = g.getGridEl(), cm = this.cm,
56574                 expandCol = g.autoExpandColumn,
56575                 gv = this;
56576         //c.beginMeasure();
56577
56578         if(!c.dom.offsetWidth){ // display:none?
56579             if(initialRender){
56580                 this.lockedWrap.show();
56581                 this.mainWrap.show();
56582             }
56583             return;
56584         }
56585
56586         var hasLock = this.cm.isLocked(0);
56587
56588         var tbh = this.headerPanel.getHeight();
56589         var bbh = this.footerPanel.getHeight();
56590
56591         if(auto){
56592             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56593             var newHeight = ch + c.getBorderWidth("tb");
56594             if(g.maxHeight){
56595                 newHeight = Math.min(g.maxHeight, newHeight);
56596             }
56597             c.setHeight(newHeight);
56598         }
56599
56600         if(g.autoWidth){
56601             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56602         }
56603
56604         var s = this.scroller;
56605
56606         var csize = c.getSize(true);
56607
56608         this.el.setSize(csize.width, csize.height);
56609
56610         this.headerPanel.setWidth(csize.width);
56611         this.footerPanel.setWidth(csize.width);
56612
56613         var hdHeight = this.mainHd.getHeight();
56614         var vw = csize.width;
56615         var vh = csize.height - (tbh + bbh);
56616
56617         s.setSize(vw, vh);
56618
56619         var bt = this.getBodyTable();
56620         
56621         if(cm.getLockedCount() == cm.config.length){
56622             bt = this.getLockedTable();
56623         }
56624         
56625         var ltWidth = hasLock ?
56626                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56627
56628         var scrollHeight = bt.offsetHeight;
56629         var scrollWidth = ltWidth + bt.offsetWidth;
56630         var vscroll = false, hscroll = false;
56631
56632         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56633
56634         var lw = this.lockedWrap, mw = this.mainWrap;
56635         var lb = this.lockedBody, mb = this.mainBody;
56636
56637         setTimeout(function(){
56638             var t = s.dom.offsetTop;
56639             var w = s.dom.clientWidth,
56640                 h = s.dom.clientHeight;
56641
56642             lw.setTop(t);
56643             lw.setSize(ltWidth, h);
56644
56645             mw.setLeftTop(ltWidth, t);
56646             mw.setSize(w-ltWidth, h);
56647
56648             lb.setHeight(h-hdHeight);
56649             mb.setHeight(h-hdHeight);
56650
56651             if(is2ndPass !== true && !gv.userResized && expandCol){
56652                 // high speed resize without full column calculation
56653                 
56654                 var ci = cm.getIndexById(expandCol);
56655                 if (ci < 0) {
56656                     ci = cm.findColumnIndex(expandCol);
56657                 }
56658                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56659                 var expandId = cm.getColumnId(ci);
56660                 var  tw = cm.getTotalWidth(false);
56661                 var currentWidth = cm.getColumnWidth(ci);
56662                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56663                 if(currentWidth != cw){
56664                     cm.setColumnWidth(ci, cw, true);
56665                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56666                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56667                     gv.updateSplitters();
56668                     gv.layout(false, true);
56669                 }
56670             }
56671
56672             if(initialRender){
56673                 lw.show();
56674                 mw.show();
56675             }
56676             //c.endMeasure();
56677         }, 10);
56678     },
56679
56680     onWindowResize : function(){
56681         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56682             return;
56683         }
56684         this.layout();
56685     },
56686
56687     appendFooter : function(parentEl){
56688         return null;
56689     },
56690
56691     sortAscText : "Sort Ascending",
56692     sortDescText : "Sort Descending",
56693     lockText : "Lock Column",
56694     unlockText : "Unlock Column",
56695     columnsText : "Columns",
56696  
56697     columnsWiderText : "Wider",
56698     columnsNarrowText : "Thinner"
56699 });
56700
56701
56702 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56703     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56704     this.proxy.el.addClass('x-grid3-col-dd');
56705 };
56706
56707 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56708     handleMouseDown : function(e){
56709
56710     },
56711
56712     callHandleMouseDown : function(e){
56713         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56714     }
56715 });
56716 /*
56717  * Based on:
56718  * Ext JS Library 1.1.1
56719  * Copyright(c) 2006-2007, Ext JS, LLC.
56720  *
56721  * Originally Released Under LGPL - original licence link has changed is not relivant.
56722  *
56723  * Fork - LGPL
56724  * <script type="text/javascript">
56725  */
56726  
56727 // private
56728 // This is a support class used internally by the Grid components
56729 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56730     this.grid = grid;
56731     this.view = grid.getView();
56732     this.proxy = this.view.resizeProxy;
56733     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56734         "gridSplitters" + this.grid.getGridEl().id, {
56735         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56736     });
56737     this.setHandleElId(Roo.id(hd));
56738     this.setOuterHandleElId(Roo.id(hd2));
56739     this.scroll = false;
56740 };
56741 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56742     fly: Roo.Element.fly,
56743
56744     b4StartDrag : function(x, y){
56745         this.view.headersDisabled = true;
56746         this.proxy.setHeight(this.view.mainWrap.getHeight());
56747         var w = this.cm.getColumnWidth(this.cellIndex);
56748         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56749         this.resetConstraints();
56750         this.setXConstraint(minw, 1000);
56751         this.setYConstraint(0, 0);
56752         this.minX = x - minw;
56753         this.maxX = x + 1000;
56754         this.startPos = x;
56755         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56756     },
56757
56758
56759     handleMouseDown : function(e){
56760         ev = Roo.EventObject.setEvent(e);
56761         var t = this.fly(ev.getTarget());
56762         if(t.hasClass("x-grid-split")){
56763             this.cellIndex = this.view.getCellIndex(t.dom);
56764             this.split = t.dom;
56765             this.cm = this.grid.colModel;
56766             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56767                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56768             }
56769         }
56770     },
56771
56772     endDrag : function(e){
56773         this.view.headersDisabled = false;
56774         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56775         var diff = endX - this.startPos;
56776         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56777     },
56778
56779     autoOffset : function(){
56780         this.setDelta(0,0);
56781     }
56782 });/*
56783  * Based on:
56784  * Ext JS Library 1.1.1
56785  * Copyright(c) 2006-2007, Ext JS, LLC.
56786  *
56787  * Originally Released Under LGPL - original licence link has changed is not relivant.
56788  *
56789  * Fork - LGPL
56790  * <script type="text/javascript">
56791  */
56792  
56793 // private
56794 // This is a support class used internally by the Grid components
56795 Roo.grid.GridDragZone = function(grid, config){
56796     this.view = grid.getView();
56797     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56798     if(this.view.lockedBody){
56799         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56800         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56801     }
56802     this.scroll = false;
56803     this.grid = grid;
56804     this.ddel = document.createElement('div');
56805     this.ddel.className = 'x-grid-dd-wrap';
56806 };
56807
56808 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56809     ddGroup : "GridDD",
56810
56811     getDragData : function(e){
56812         var t = Roo.lib.Event.getTarget(e);
56813         var rowIndex = this.view.findRowIndex(t);
56814         var sm = this.grid.selModel;
56815             
56816         //Roo.log(rowIndex);
56817         
56818         if (sm.getSelectedCell) {
56819             // cell selection..
56820             if (!sm.getSelectedCell()) {
56821                 return false;
56822             }
56823             if (rowIndex != sm.getSelectedCell()[0]) {
56824                 return false;
56825             }
56826         
56827         }
56828         
56829         if(rowIndex !== false){
56830             
56831             // if editorgrid.. 
56832             
56833             
56834             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56835                
56836             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56837               //  
56838             //}
56839             if (e.hasModifier()){
56840                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56841             }
56842             
56843             Roo.log("getDragData");
56844             
56845             return {
56846                 grid: this.grid,
56847                 ddel: this.ddel,
56848                 rowIndex: rowIndex,
56849                 selections:sm.getSelections ? sm.getSelections() : (
56850                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56851                 )
56852             };
56853         }
56854         return false;
56855     },
56856
56857     onInitDrag : function(e){
56858         var data = this.dragData;
56859         this.ddel.innerHTML = this.grid.getDragDropText();
56860         this.proxy.update(this.ddel);
56861         // fire start drag?
56862     },
56863
56864     afterRepair : function(){
56865         this.dragging = false;
56866     },
56867
56868     getRepairXY : function(e, data){
56869         return false;
56870     },
56871
56872     onEndDrag : function(data, e){
56873         // fire end drag?
56874     },
56875
56876     onValidDrop : function(dd, e, id){
56877         // fire drag drop?
56878         this.hideProxy();
56879     },
56880
56881     beforeInvalidDrop : function(e, id){
56882
56883     }
56884 });/*
56885  * Based on:
56886  * Ext JS Library 1.1.1
56887  * Copyright(c) 2006-2007, Ext JS, LLC.
56888  *
56889  * Originally Released Under LGPL - original licence link has changed is not relivant.
56890  *
56891  * Fork - LGPL
56892  * <script type="text/javascript">
56893  */
56894  
56895
56896 /**
56897  * @class Roo.grid.ColumnModel
56898  * @extends Roo.util.Observable
56899  * This is the default implementation of a ColumnModel used by the Grid. It defines
56900  * the columns in the grid.
56901  * <br>Usage:<br>
56902  <pre><code>
56903  var colModel = new Roo.grid.ColumnModel([
56904         {header: "Ticker", width: 60, sortable: true, locked: true},
56905         {header: "Company Name", width: 150, sortable: true},
56906         {header: "Market Cap.", width: 100, sortable: true},
56907         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56908         {header: "Employees", width: 100, sortable: true, resizable: false}
56909  ]);
56910  </code></pre>
56911  * <p>
56912  
56913  * The config options listed for this class are options which may appear in each
56914  * individual column definition.
56915  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56916  * @constructor
56917  * @param {Object} config An Array of column config objects. See this class's
56918  * config objects for details.
56919 */
56920 Roo.grid.ColumnModel = function(config){
56921         /**
56922      * The config passed into the constructor
56923      */
56924     this.config = config;
56925     this.lookup = {};
56926
56927     // if no id, create one
56928     // if the column does not have a dataIndex mapping,
56929     // map it to the order it is in the config
56930     for(var i = 0, len = config.length; i < len; i++){
56931         var c = config[i];
56932         if(typeof c.dataIndex == "undefined"){
56933             c.dataIndex = i;
56934         }
56935         if(typeof c.renderer == "string"){
56936             c.renderer = Roo.util.Format[c.renderer];
56937         }
56938         if(typeof c.id == "undefined"){
56939             c.id = Roo.id();
56940         }
56941         if(c.editor && c.editor.xtype){
56942             c.editor  = Roo.factory(c.editor, Roo.grid);
56943         }
56944         if(c.editor && c.editor.isFormField){
56945             c.editor = new Roo.grid.GridEditor(c.editor);
56946         }
56947         this.lookup[c.id] = c;
56948     }
56949
56950     /**
56951      * The width of columns which have no width specified (defaults to 100)
56952      * @type Number
56953      */
56954     this.defaultWidth = 100;
56955
56956     /**
56957      * Default sortable of columns which have no sortable specified (defaults to false)
56958      * @type Boolean
56959      */
56960     this.defaultSortable = false;
56961
56962     this.addEvents({
56963         /**
56964              * @event widthchange
56965              * Fires when the width of a column changes.
56966              * @param {ColumnModel} this
56967              * @param {Number} columnIndex The column index
56968              * @param {Number} newWidth The new width
56969              */
56970             "widthchange": true,
56971         /**
56972              * @event headerchange
56973              * Fires when the text of a header changes.
56974              * @param {ColumnModel} this
56975              * @param {Number} columnIndex The column index
56976              * @param {Number} newText The new header text
56977              */
56978             "headerchange": true,
56979         /**
56980              * @event hiddenchange
56981              * Fires when a column is hidden or "unhidden".
56982              * @param {ColumnModel} this
56983              * @param {Number} columnIndex The column index
56984              * @param {Boolean} hidden true if hidden, false otherwise
56985              */
56986             "hiddenchange": true,
56987             /**
56988          * @event columnmoved
56989          * Fires when a column is moved.
56990          * @param {ColumnModel} this
56991          * @param {Number} oldIndex
56992          * @param {Number} newIndex
56993          */
56994         "columnmoved" : true,
56995         /**
56996          * @event columlockchange
56997          * Fires when a column's locked state is changed
56998          * @param {ColumnModel} this
56999          * @param {Number} colIndex
57000          * @param {Boolean} locked true if locked
57001          */
57002         "columnlockchange" : true
57003     });
57004     Roo.grid.ColumnModel.superclass.constructor.call(this);
57005 };
57006 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57007     /**
57008      * @cfg {String} header The header text to display in the Grid view.
57009      */
57010     /**
57011      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57012      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57013      * specified, the column's index is used as an index into the Record's data Array.
57014      */
57015     /**
57016      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57017      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57018      */
57019     /**
57020      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57021      * Defaults to the value of the {@link #defaultSortable} property.
57022      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57023      */
57024     /**
57025      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57026      */
57027     /**
57028      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57029      */
57030     /**
57031      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57032      */
57033     /**
57034      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57035      */
57036     /**
57037      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57038      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57039      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57040      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57041      */
57042        /**
57043      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57044      */
57045     /**
57046      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57047      */
57048     /**
57049      * @cfg {String} cursor (Optional)
57050      */
57051     /**
57052      * @cfg {String} tooltip (Optional)
57053      */
57054     /**
57055      * @cfg {Number} xs (Optional)
57056      */
57057     /**
57058      * @cfg {Number} sm (Optional)
57059      */
57060     /**
57061      * @cfg {Number} md (Optional)
57062      */
57063     /**
57064      * @cfg {Number} lg (Optional)
57065      */
57066     /**
57067      * Returns the id of the column at the specified index.
57068      * @param {Number} index The column index
57069      * @return {String} the id
57070      */
57071     getColumnId : function(index){
57072         return this.config[index].id;
57073     },
57074
57075     /**
57076      * Returns the column for a specified id.
57077      * @param {String} id The column id
57078      * @return {Object} the column
57079      */
57080     getColumnById : function(id){
57081         return this.lookup[id];
57082     },
57083
57084     
57085     /**
57086      * Returns the column for a specified dataIndex.
57087      * @param {String} dataIndex The column dataIndex
57088      * @return {Object|Boolean} the column or false if not found
57089      */
57090     getColumnByDataIndex: function(dataIndex){
57091         var index = this.findColumnIndex(dataIndex);
57092         return index > -1 ? this.config[index] : false;
57093     },
57094     
57095     /**
57096      * Returns the index for a specified column id.
57097      * @param {String} id The column id
57098      * @return {Number} the index, or -1 if not found
57099      */
57100     getIndexById : function(id){
57101         for(var i = 0, len = this.config.length; i < len; i++){
57102             if(this.config[i].id == id){
57103                 return i;
57104             }
57105         }
57106         return -1;
57107     },
57108     
57109     /**
57110      * Returns the index for a specified column dataIndex.
57111      * @param {String} dataIndex The column dataIndex
57112      * @return {Number} the index, or -1 if not found
57113      */
57114     
57115     findColumnIndex : function(dataIndex){
57116         for(var i = 0, len = this.config.length; i < len; i++){
57117             if(this.config[i].dataIndex == dataIndex){
57118                 return i;
57119             }
57120         }
57121         return -1;
57122     },
57123     
57124     
57125     moveColumn : function(oldIndex, newIndex){
57126         var c = this.config[oldIndex];
57127         this.config.splice(oldIndex, 1);
57128         this.config.splice(newIndex, 0, c);
57129         this.dataMap = null;
57130         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57131     },
57132
57133     isLocked : function(colIndex){
57134         return this.config[colIndex].locked === true;
57135     },
57136
57137     setLocked : function(colIndex, value, suppressEvent){
57138         if(this.isLocked(colIndex) == value){
57139             return;
57140         }
57141         this.config[colIndex].locked = value;
57142         if(!suppressEvent){
57143             this.fireEvent("columnlockchange", this, colIndex, value);
57144         }
57145     },
57146
57147     getTotalLockedWidth : function(){
57148         var totalWidth = 0;
57149         for(var i = 0; i < this.config.length; i++){
57150             if(this.isLocked(i) && !this.isHidden(i)){
57151                 this.totalWidth += this.getColumnWidth(i);
57152             }
57153         }
57154         return totalWidth;
57155     },
57156
57157     getLockedCount : function(){
57158         for(var i = 0, len = this.config.length; i < len; i++){
57159             if(!this.isLocked(i)){
57160                 return i;
57161             }
57162         }
57163         
57164         return this.config.length;
57165     },
57166
57167     /**
57168      * Returns the number of columns.
57169      * @return {Number}
57170      */
57171     getColumnCount : function(visibleOnly){
57172         if(visibleOnly === true){
57173             var c = 0;
57174             for(var i = 0, len = this.config.length; i < len; i++){
57175                 if(!this.isHidden(i)){
57176                     c++;
57177                 }
57178             }
57179             return c;
57180         }
57181         return this.config.length;
57182     },
57183
57184     /**
57185      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57186      * @param {Function} fn
57187      * @param {Object} scope (optional)
57188      * @return {Array} result
57189      */
57190     getColumnsBy : function(fn, scope){
57191         var r = [];
57192         for(var i = 0, len = this.config.length; i < len; i++){
57193             var c = this.config[i];
57194             if(fn.call(scope||this, c, i) === true){
57195                 r[r.length] = c;
57196             }
57197         }
57198         return r;
57199     },
57200
57201     /**
57202      * Returns true if the specified column is sortable.
57203      * @param {Number} col The column index
57204      * @return {Boolean}
57205      */
57206     isSortable : function(col){
57207         if(typeof this.config[col].sortable == "undefined"){
57208             return this.defaultSortable;
57209         }
57210         return this.config[col].sortable;
57211     },
57212
57213     /**
57214      * Returns the rendering (formatting) function defined for the column.
57215      * @param {Number} col The column index.
57216      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57217      */
57218     getRenderer : function(col){
57219         if(!this.config[col].renderer){
57220             return Roo.grid.ColumnModel.defaultRenderer;
57221         }
57222         return this.config[col].renderer;
57223     },
57224
57225     /**
57226      * Sets the rendering (formatting) function for a column.
57227      * @param {Number} col The column index
57228      * @param {Function} fn The function to use to process the cell's raw data
57229      * to return HTML markup for the grid view. The render function is called with
57230      * the following parameters:<ul>
57231      * <li>Data value.</li>
57232      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57233      * <li>css A CSS style string to apply to the table cell.</li>
57234      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57235      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57236      * <li>Row index</li>
57237      * <li>Column index</li>
57238      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57239      */
57240     setRenderer : function(col, fn){
57241         this.config[col].renderer = fn;
57242     },
57243
57244     /**
57245      * Returns the width for the specified column.
57246      * @param {Number} col The column index
57247      * @return {Number}
57248      */
57249     getColumnWidth : function(col){
57250         return this.config[col].width * 1 || this.defaultWidth;
57251     },
57252
57253     /**
57254      * Sets the width for a column.
57255      * @param {Number} col The column index
57256      * @param {Number} width The new width
57257      */
57258     setColumnWidth : function(col, width, suppressEvent){
57259         this.config[col].width = width;
57260         this.totalWidth = null;
57261         if(!suppressEvent){
57262              this.fireEvent("widthchange", this, col, width);
57263         }
57264     },
57265
57266     /**
57267      * Returns the total width of all columns.
57268      * @param {Boolean} includeHidden True to include hidden column widths
57269      * @return {Number}
57270      */
57271     getTotalWidth : function(includeHidden){
57272         if(!this.totalWidth){
57273             this.totalWidth = 0;
57274             for(var i = 0, len = this.config.length; i < len; i++){
57275                 if(includeHidden || !this.isHidden(i)){
57276                     this.totalWidth += this.getColumnWidth(i);
57277                 }
57278             }
57279         }
57280         return this.totalWidth;
57281     },
57282
57283     /**
57284      * Returns the header for the specified column.
57285      * @param {Number} col The column index
57286      * @return {String}
57287      */
57288     getColumnHeader : function(col){
57289         return this.config[col].header;
57290     },
57291
57292     /**
57293      * Sets the header for a column.
57294      * @param {Number} col The column index
57295      * @param {String} header The new header
57296      */
57297     setColumnHeader : function(col, header){
57298         this.config[col].header = header;
57299         this.fireEvent("headerchange", this, col, header);
57300     },
57301
57302     /**
57303      * Returns the tooltip for the specified column.
57304      * @param {Number} col The column index
57305      * @return {String}
57306      */
57307     getColumnTooltip : function(col){
57308             return this.config[col].tooltip;
57309     },
57310     /**
57311      * Sets the tooltip for a column.
57312      * @param {Number} col The column index
57313      * @param {String} tooltip The new tooltip
57314      */
57315     setColumnTooltip : function(col, tooltip){
57316             this.config[col].tooltip = tooltip;
57317     },
57318
57319     /**
57320      * Returns the dataIndex for the specified column.
57321      * @param {Number} col The column index
57322      * @return {Number}
57323      */
57324     getDataIndex : function(col){
57325         return this.config[col].dataIndex;
57326     },
57327
57328     /**
57329      * Sets the dataIndex for a column.
57330      * @param {Number} col The column index
57331      * @param {Number} dataIndex The new dataIndex
57332      */
57333     setDataIndex : function(col, dataIndex){
57334         this.config[col].dataIndex = dataIndex;
57335     },
57336
57337     
57338     
57339     /**
57340      * Returns true if the cell is editable.
57341      * @param {Number} colIndex The column index
57342      * @param {Number} rowIndex The row index - this is nto actually used..?
57343      * @return {Boolean}
57344      */
57345     isCellEditable : function(colIndex, rowIndex){
57346         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57347     },
57348
57349     /**
57350      * Returns the editor defined for the cell/column.
57351      * return false or null to disable editing.
57352      * @param {Number} colIndex The column index
57353      * @param {Number} rowIndex The row index
57354      * @return {Object}
57355      */
57356     getCellEditor : function(colIndex, rowIndex){
57357         return this.config[colIndex].editor;
57358     },
57359
57360     /**
57361      * Sets if a column is editable.
57362      * @param {Number} col The column index
57363      * @param {Boolean} editable True if the column is editable
57364      */
57365     setEditable : function(col, editable){
57366         this.config[col].editable = editable;
57367     },
57368
57369
57370     /**
57371      * Returns true if the column is hidden.
57372      * @param {Number} colIndex The column index
57373      * @return {Boolean}
57374      */
57375     isHidden : function(colIndex){
57376         return this.config[colIndex].hidden;
57377     },
57378
57379
57380     /**
57381      * Returns true if the column width cannot be changed
57382      */
57383     isFixed : function(colIndex){
57384         return this.config[colIndex].fixed;
57385     },
57386
57387     /**
57388      * Returns true if the column can be resized
57389      * @return {Boolean}
57390      */
57391     isResizable : function(colIndex){
57392         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57393     },
57394     /**
57395      * Sets if a column is hidden.
57396      * @param {Number} colIndex The column index
57397      * @param {Boolean} hidden True if the column is hidden
57398      */
57399     setHidden : function(colIndex, hidden){
57400         this.config[colIndex].hidden = hidden;
57401         this.totalWidth = null;
57402         this.fireEvent("hiddenchange", this, colIndex, hidden);
57403     },
57404
57405     /**
57406      * Sets the editor for a column.
57407      * @param {Number} col The column index
57408      * @param {Object} editor The editor object
57409      */
57410     setEditor : function(col, editor){
57411         this.config[col].editor = editor;
57412     }
57413 });
57414
57415 Roo.grid.ColumnModel.defaultRenderer = function(value)
57416 {
57417     if(typeof value == "object") {
57418         return value;
57419     }
57420         if(typeof value == "string" && value.length < 1){
57421             return "&#160;";
57422         }
57423     
57424         return String.format("{0}", value);
57425 };
57426
57427 // Alias for backwards compatibility
57428 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57429 /*
57430  * Based on:
57431  * Ext JS Library 1.1.1
57432  * Copyright(c) 2006-2007, Ext JS, LLC.
57433  *
57434  * Originally Released Under LGPL - original licence link has changed is not relivant.
57435  *
57436  * Fork - LGPL
57437  * <script type="text/javascript">
57438  */
57439
57440 /**
57441  * @class Roo.grid.AbstractSelectionModel
57442  * @extends Roo.util.Observable
57443  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57444  * implemented by descendant classes.  This class should not be directly instantiated.
57445  * @constructor
57446  */
57447 Roo.grid.AbstractSelectionModel = function(){
57448     this.locked = false;
57449     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57450 };
57451
57452 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57453     /** @ignore Called by the grid automatically. Do not call directly. */
57454     init : function(grid){
57455         this.grid = grid;
57456         this.initEvents();
57457     },
57458
57459     /**
57460      * Locks the selections.
57461      */
57462     lock : function(){
57463         this.locked = true;
57464     },
57465
57466     /**
57467      * Unlocks the selections.
57468      */
57469     unlock : function(){
57470         this.locked = false;
57471     },
57472
57473     /**
57474      * Returns true if the selections are locked.
57475      * @return {Boolean}
57476      */
57477     isLocked : function(){
57478         return this.locked;
57479     }
57480 });/*
57481  * Based on:
57482  * Ext JS Library 1.1.1
57483  * Copyright(c) 2006-2007, Ext JS, LLC.
57484  *
57485  * Originally Released Under LGPL - original licence link has changed is not relivant.
57486  *
57487  * Fork - LGPL
57488  * <script type="text/javascript">
57489  */
57490 /**
57491  * @extends Roo.grid.AbstractSelectionModel
57492  * @class Roo.grid.RowSelectionModel
57493  * The default SelectionModel used by {@link Roo.grid.Grid}.
57494  * It supports multiple selections and keyboard selection/navigation. 
57495  * @constructor
57496  * @param {Object} config
57497  */
57498 Roo.grid.RowSelectionModel = function(config){
57499     Roo.apply(this, config);
57500     this.selections = new Roo.util.MixedCollection(false, function(o){
57501         return o.id;
57502     });
57503
57504     this.last = false;
57505     this.lastActive = false;
57506
57507     this.addEvents({
57508         /**
57509              * @event selectionchange
57510              * Fires when the selection changes
57511              * @param {SelectionModel} this
57512              */
57513             "selectionchange" : true,
57514         /**
57515              * @event afterselectionchange
57516              * Fires after the selection changes (eg. by key press or clicking)
57517              * @param {SelectionModel} this
57518              */
57519             "afterselectionchange" : true,
57520         /**
57521              * @event beforerowselect
57522              * Fires when a row is selected being selected, return false to cancel.
57523              * @param {SelectionModel} this
57524              * @param {Number} rowIndex The selected index
57525              * @param {Boolean} keepExisting False if other selections will be cleared
57526              */
57527             "beforerowselect" : true,
57528         /**
57529              * @event rowselect
57530              * Fires when a row is selected.
57531              * @param {SelectionModel} this
57532              * @param {Number} rowIndex The selected index
57533              * @param {Roo.data.Record} r The record
57534              */
57535             "rowselect" : true,
57536         /**
57537              * @event rowdeselect
57538              * Fires when a row is deselected.
57539              * @param {SelectionModel} this
57540              * @param {Number} rowIndex The selected index
57541              */
57542         "rowdeselect" : true
57543     });
57544     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57545     this.locked = false;
57546 };
57547
57548 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57549     /**
57550      * @cfg {Boolean} singleSelect
57551      * True to allow selection of only one row at a time (defaults to false)
57552      */
57553     singleSelect : false,
57554
57555     // private
57556     initEvents : function(){
57557
57558         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57559             this.grid.on("mousedown", this.handleMouseDown, this);
57560         }else{ // allow click to work like normal
57561             this.grid.on("rowclick", this.handleDragableRowClick, this);
57562         }
57563
57564         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57565             "up" : function(e){
57566                 if(!e.shiftKey){
57567                     this.selectPrevious(e.shiftKey);
57568                 }else if(this.last !== false && this.lastActive !== false){
57569                     var last = this.last;
57570                     this.selectRange(this.last,  this.lastActive-1);
57571                     this.grid.getView().focusRow(this.lastActive);
57572                     if(last !== false){
57573                         this.last = last;
57574                     }
57575                 }else{
57576                     this.selectFirstRow();
57577                 }
57578                 this.fireEvent("afterselectionchange", this);
57579             },
57580             "down" : function(e){
57581                 if(!e.shiftKey){
57582                     this.selectNext(e.shiftKey);
57583                 }else if(this.last !== false && this.lastActive !== false){
57584                     var last = this.last;
57585                     this.selectRange(this.last,  this.lastActive+1);
57586                     this.grid.getView().focusRow(this.lastActive);
57587                     if(last !== false){
57588                         this.last = last;
57589                     }
57590                 }else{
57591                     this.selectFirstRow();
57592                 }
57593                 this.fireEvent("afterselectionchange", this);
57594             },
57595             scope: this
57596         });
57597
57598         var view = this.grid.view;
57599         view.on("refresh", this.onRefresh, this);
57600         view.on("rowupdated", this.onRowUpdated, this);
57601         view.on("rowremoved", this.onRemove, this);
57602     },
57603
57604     // private
57605     onRefresh : function(){
57606         var ds = this.grid.dataSource, i, v = this.grid.view;
57607         var s = this.selections;
57608         s.each(function(r){
57609             if((i = ds.indexOfId(r.id)) != -1){
57610                 v.onRowSelect(i);
57611                 s.add(ds.getAt(i)); // updating the selection relate data
57612             }else{
57613                 s.remove(r);
57614             }
57615         });
57616     },
57617
57618     // private
57619     onRemove : function(v, index, r){
57620         this.selections.remove(r);
57621     },
57622
57623     // private
57624     onRowUpdated : function(v, index, r){
57625         if(this.isSelected(r)){
57626             v.onRowSelect(index);
57627         }
57628     },
57629
57630     /**
57631      * Select records.
57632      * @param {Array} records The records to select
57633      * @param {Boolean} keepExisting (optional) True to keep existing selections
57634      */
57635     selectRecords : function(records, keepExisting){
57636         if(!keepExisting){
57637             this.clearSelections();
57638         }
57639         var ds = this.grid.dataSource;
57640         for(var i = 0, len = records.length; i < len; i++){
57641             this.selectRow(ds.indexOf(records[i]), true);
57642         }
57643     },
57644
57645     /**
57646      * Gets the number of selected rows.
57647      * @return {Number}
57648      */
57649     getCount : function(){
57650         return this.selections.length;
57651     },
57652
57653     /**
57654      * Selects the first row in the grid.
57655      */
57656     selectFirstRow : function(){
57657         this.selectRow(0);
57658     },
57659
57660     /**
57661      * Select the last row.
57662      * @param {Boolean} keepExisting (optional) True to keep existing selections
57663      */
57664     selectLastRow : function(keepExisting){
57665         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57666     },
57667
57668     /**
57669      * Selects the row immediately following the last selected row.
57670      * @param {Boolean} keepExisting (optional) True to keep existing selections
57671      */
57672     selectNext : function(keepExisting){
57673         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57674             this.selectRow(this.last+1, keepExisting);
57675             this.grid.getView().focusRow(this.last);
57676         }
57677     },
57678
57679     /**
57680      * Selects the row that precedes the last selected row.
57681      * @param {Boolean} keepExisting (optional) True to keep existing selections
57682      */
57683     selectPrevious : function(keepExisting){
57684         if(this.last){
57685             this.selectRow(this.last-1, keepExisting);
57686             this.grid.getView().focusRow(this.last);
57687         }
57688     },
57689
57690     /**
57691      * Returns the selected records
57692      * @return {Array} Array of selected records
57693      */
57694     getSelections : function(){
57695         return [].concat(this.selections.items);
57696     },
57697
57698     /**
57699      * Returns the first selected record.
57700      * @return {Record}
57701      */
57702     getSelected : function(){
57703         return this.selections.itemAt(0);
57704     },
57705
57706
57707     /**
57708      * Clears all selections.
57709      */
57710     clearSelections : function(fast){
57711         if(this.locked) {
57712             return;
57713         }
57714         if(fast !== true){
57715             var ds = this.grid.dataSource;
57716             var s = this.selections;
57717             s.each(function(r){
57718                 this.deselectRow(ds.indexOfId(r.id));
57719             }, this);
57720             s.clear();
57721         }else{
57722             this.selections.clear();
57723         }
57724         this.last = false;
57725     },
57726
57727
57728     /**
57729      * Selects all rows.
57730      */
57731     selectAll : function(){
57732         if(this.locked) {
57733             return;
57734         }
57735         this.selections.clear();
57736         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57737             this.selectRow(i, true);
57738         }
57739     },
57740
57741     /**
57742      * Returns True if there is a selection.
57743      * @return {Boolean}
57744      */
57745     hasSelection : function(){
57746         return this.selections.length > 0;
57747     },
57748
57749     /**
57750      * Returns True if the specified row is selected.
57751      * @param {Number/Record} record The record or index of the record to check
57752      * @return {Boolean}
57753      */
57754     isSelected : function(index){
57755         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57756         return (r && this.selections.key(r.id) ? true : false);
57757     },
57758
57759     /**
57760      * Returns True if the specified record id is selected.
57761      * @param {String} id The id of record to check
57762      * @return {Boolean}
57763      */
57764     isIdSelected : function(id){
57765         return (this.selections.key(id) ? true : false);
57766     },
57767
57768     // private
57769     handleMouseDown : function(e, t){
57770         var view = this.grid.getView(), rowIndex;
57771         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57772             return;
57773         };
57774         if(e.shiftKey && this.last !== false){
57775             var last = this.last;
57776             this.selectRange(last, rowIndex, e.ctrlKey);
57777             this.last = last; // reset the last
57778             view.focusRow(rowIndex);
57779         }else{
57780             var isSelected = this.isSelected(rowIndex);
57781             if(e.button !== 0 && isSelected){
57782                 view.focusRow(rowIndex);
57783             }else if(e.ctrlKey && isSelected){
57784                 this.deselectRow(rowIndex);
57785             }else if(!isSelected){
57786                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57787                 view.focusRow(rowIndex);
57788             }
57789         }
57790         this.fireEvent("afterselectionchange", this);
57791     },
57792     // private
57793     handleDragableRowClick :  function(grid, rowIndex, e) 
57794     {
57795         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57796             this.selectRow(rowIndex, false);
57797             grid.view.focusRow(rowIndex);
57798              this.fireEvent("afterselectionchange", this);
57799         }
57800     },
57801     
57802     /**
57803      * Selects multiple rows.
57804      * @param {Array} rows Array of the indexes of the row to select
57805      * @param {Boolean} keepExisting (optional) True to keep existing selections
57806      */
57807     selectRows : function(rows, keepExisting){
57808         if(!keepExisting){
57809             this.clearSelections();
57810         }
57811         for(var i = 0, len = rows.length; i < len; i++){
57812             this.selectRow(rows[i], true);
57813         }
57814     },
57815
57816     /**
57817      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57818      * @param {Number} startRow The index of the first row in the range
57819      * @param {Number} endRow The index of the last row in the range
57820      * @param {Boolean} keepExisting (optional) True to retain existing selections
57821      */
57822     selectRange : function(startRow, endRow, keepExisting){
57823         if(this.locked) {
57824             return;
57825         }
57826         if(!keepExisting){
57827             this.clearSelections();
57828         }
57829         if(startRow <= endRow){
57830             for(var i = startRow; i <= endRow; i++){
57831                 this.selectRow(i, true);
57832             }
57833         }else{
57834             for(var i = startRow; i >= endRow; i--){
57835                 this.selectRow(i, true);
57836             }
57837         }
57838     },
57839
57840     /**
57841      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57842      * @param {Number} startRow The index of the first row in the range
57843      * @param {Number} endRow The index of the last row in the range
57844      */
57845     deselectRange : function(startRow, endRow, preventViewNotify){
57846         if(this.locked) {
57847             return;
57848         }
57849         for(var i = startRow; i <= endRow; i++){
57850             this.deselectRow(i, preventViewNotify);
57851         }
57852     },
57853
57854     /**
57855      * Selects a row.
57856      * @param {Number} row The index of the row to select
57857      * @param {Boolean} keepExisting (optional) True to keep existing selections
57858      */
57859     selectRow : function(index, keepExisting, preventViewNotify){
57860         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57861             return;
57862         }
57863         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57864             if(!keepExisting || this.singleSelect){
57865                 this.clearSelections();
57866             }
57867             var r = this.grid.dataSource.getAt(index);
57868             this.selections.add(r);
57869             this.last = this.lastActive = index;
57870             if(!preventViewNotify){
57871                 this.grid.getView().onRowSelect(index);
57872             }
57873             this.fireEvent("rowselect", this, index, r);
57874             this.fireEvent("selectionchange", this);
57875         }
57876     },
57877
57878     /**
57879      * Deselects a row.
57880      * @param {Number} row The index of the row to deselect
57881      */
57882     deselectRow : function(index, preventViewNotify){
57883         if(this.locked) {
57884             return;
57885         }
57886         if(this.last == index){
57887             this.last = false;
57888         }
57889         if(this.lastActive == index){
57890             this.lastActive = false;
57891         }
57892         var r = this.grid.dataSource.getAt(index);
57893         this.selections.remove(r);
57894         if(!preventViewNotify){
57895             this.grid.getView().onRowDeselect(index);
57896         }
57897         this.fireEvent("rowdeselect", this, index);
57898         this.fireEvent("selectionchange", this);
57899     },
57900
57901     // private
57902     restoreLast : function(){
57903         if(this._last){
57904             this.last = this._last;
57905         }
57906     },
57907
57908     // private
57909     acceptsNav : function(row, col, cm){
57910         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57911     },
57912
57913     // private
57914     onEditorKey : function(field, e){
57915         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57916         if(k == e.TAB){
57917             e.stopEvent();
57918             ed.completeEdit();
57919             if(e.shiftKey){
57920                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57921             }else{
57922                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57923             }
57924         }else if(k == e.ENTER && !e.ctrlKey){
57925             e.stopEvent();
57926             ed.completeEdit();
57927             if(e.shiftKey){
57928                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57929             }else{
57930                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57931             }
57932         }else if(k == e.ESC){
57933             ed.cancelEdit();
57934         }
57935         if(newCell){
57936             g.startEditing(newCell[0], newCell[1]);
57937         }
57938     }
57939 });/*
57940  * Based on:
57941  * Ext JS Library 1.1.1
57942  * Copyright(c) 2006-2007, Ext JS, LLC.
57943  *
57944  * Originally Released Under LGPL - original licence link has changed is not relivant.
57945  *
57946  * Fork - LGPL
57947  * <script type="text/javascript">
57948  */
57949 /**
57950  * @class Roo.grid.CellSelectionModel
57951  * @extends Roo.grid.AbstractSelectionModel
57952  * This class provides the basic implementation for cell selection in a grid.
57953  * @constructor
57954  * @param {Object} config The object containing the configuration of this model.
57955  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
57956  */
57957 Roo.grid.CellSelectionModel = function(config){
57958     Roo.apply(this, config);
57959
57960     this.selection = null;
57961
57962     this.addEvents({
57963         /**
57964              * @event beforerowselect
57965              * Fires before a cell is selected.
57966              * @param {SelectionModel} this
57967              * @param {Number} rowIndex The selected row index
57968              * @param {Number} colIndex The selected cell index
57969              */
57970             "beforecellselect" : true,
57971         /**
57972              * @event cellselect
57973              * Fires when a cell is selected.
57974              * @param {SelectionModel} this
57975              * @param {Number} rowIndex The selected row index
57976              * @param {Number} colIndex The selected cell index
57977              */
57978             "cellselect" : true,
57979         /**
57980              * @event selectionchange
57981              * Fires when the active selection changes.
57982              * @param {SelectionModel} this
57983              * @param {Object} selection null for no selection or an object (o) with two properties
57984                 <ul>
57985                 <li>o.record: the record object for the row the selection is in</li>
57986                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
57987                 </ul>
57988              */
57989             "selectionchange" : true,
57990         /**
57991              * @event tabend
57992              * Fires when the tab (or enter) was pressed on the last editable cell
57993              * You can use this to trigger add new row.
57994              * @param {SelectionModel} this
57995              */
57996             "tabend" : true,
57997          /**
57998              * @event beforeeditnext
57999              * Fires before the next editable sell is made active
58000              * You can use this to skip to another cell or fire the tabend
58001              *    if you set cell to false
58002              * @param {Object} eventdata object : { cell : [ row, col ] } 
58003              */
58004             "beforeeditnext" : true
58005     });
58006     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58007 };
58008
58009 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58010     
58011     enter_is_tab: false,
58012
58013     /** @ignore */
58014     initEvents : function(){
58015         this.grid.on("mousedown", this.handleMouseDown, this);
58016         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58017         var view = this.grid.view;
58018         view.on("refresh", this.onViewChange, this);
58019         view.on("rowupdated", this.onRowUpdated, this);
58020         view.on("beforerowremoved", this.clearSelections, this);
58021         view.on("beforerowsinserted", this.clearSelections, this);
58022         if(this.grid.isEditor){
58023             this.grid.on("beforeedit", this.beforeEdit,  this);
58024         }
58025     },
58026
58027         //private
58028     beforeEdit : function(e){
58029         this.select(e.row, e.column, false, true, e.record);
58030     },
58031
58032         //private
58033     onRowUpdated : function(v, index, r){
58034         if(this.selection && this.selection.record == r){
58035             v.onCellSelect(index, this.selection.cell[1]);
58036         }
58037     },
58038
58039         //private
58040     onViewChange : function(){
58041         this.clearSelections(true);
58042     },
58043
58044         /**
58045          * Returns the currently selected cell,.
58046          * @return {Array} The selected cell (row, column) or null if none selected.
58047          */
58048     getSelectedCell : function(){
58049         return this.selection ? this.selection.cell : null;
58050     },
58051
58052     /**
58053      * Clears all selections.
58054      * @param {Boolean} true to prevent the gridview from being notified about the change.
58055      */
58056     clearSelections : function(preventNotify){
58057         var s = this.selection;
58058         if(s){
58059             if(preventNotify !== true){
58060                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58061             }
58062             this.selection = null;
58063             this.fireEvent("selectionchange", this, null);
58064         }
58065     },
58066
58067     /**
58068      * Returns true if there is a selection.
58069      * @return {Boolean}
58070      */
58071     hasSelection : function(){
58072         return this.selection ? true : false;
58073     },
58074
58075     /** @ignore */
58076     handleMouseDown : function(e, t){
58077         var v = this.grid.getView();
58078         if(this.isLocked()){
58079             return;
58080         };
58081         var row = v.findRowIndex(t);
58082         var cell = v.findCellIndex(t);
58083         if(row !== false && cell !== false){
58084             this.select(row, cell);
58085         }
58086     },
58087
58088     /**
58089      * Selects a cell.
58090      * @param {Number} rowIndex
58091      * @param {Number} collIndex
58092      */
58093     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58094         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58095             this.clearSelections();
58096             r = r || this.grid.dataSource.getAt(rowIndex);
58097             this.selection = {
58098                 record : r,
58099                 cell : [rowIndex, colIndex]
58100             };
58101             if(!preventViewNotify){
58102                 var v = this.grid.getView();
58103                 v.onCellSelect(rowIndex, colIndex);
58104                 if(preventFocus !== true){
58105                     v.focusCell(rowIndex, colIndex);
58106                 }
58107             }
58108             this.fireEvent("cellselect", this, rowIndex, colIndex);
58109             this.fireEvent("selectionchange", this, this.selection);
58110         }
58111     },
58112
58113         //private
58114     isSelectable : function(rowIndex, colIndex, cm){
58115         return !cm.isHidden(colIndex);
58116     },
58117
58118     /** @ignore */
58119     handleKeyDown : function(e){
58120         //Roo.log('Cell Sel Model handleKeyDown');
58121         if(!e.isNavKeyPress()){
58122             return;
58123         }
58124         var g = this.grid, s = this.selection;
58125         if(!s){
58126             e.stopEvent();
58127             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58128             if(cell){
58129                 this.select(cell[0], cell[1]);
58130             }
58131             return;
58132         }
58133         var sm = this;
58134         var walk = function(row, col, step){
58135             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58136         };
58137         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58138         var newCell;
58139
58140       
58141
58142         switch(k){
58143             case e.TAB:
58144                 // handled by onEditorKey
58145                 if (g.isEditor && g.editing) {
58146                     return;
58147                 }
58148                 if(e.shiftKey) {
58149                     newCell = walk(r, c-1, -1);
58150                 } else {
58151                     newCell = walk(r, c+1, 1);
58152                 }
58153                 break;
58154             
58155             case e.DOWN:
58156                newCell = walk(r+1, c, 1);
58157                 break;
58158             
58159             case e.UP:
58160                 newCell = walk(r-1, c, -1);
58161                 break;
58162             
58163             case e.RIGHT:
58164                 newCell = walk(r, c+1, 1);
58165                 break;
58166             
58167             case e.LEFT:
58168                 newCell = walk(r, c-1, -1);
58169                 break;
58170             
58171             case e.ENTER:
58172                 
58173                 if(g.isEditor && !g.editing){
58174                    g.startEditing(r, c);
58175                    e.stopEvent();
58176                    return;
58177                 }
58178                 
58179                 
58180              break;
58181         };
58182         if(newCell){
58183             this.select(newCell[0], newCell[1]);
58184             e.stopEvent();
58185             
58186         }
58187     },
58188
58189     acceptsNav : function(row, col, cm){
58190         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58191     },
58192     /**
58193      * Selects a cell.
58194      * @param {Number} field (not used) - as it's normally used as a listener
58195      * @param {Number} e - event - fake it by using
58196      *
58197      * var e = Roo.EventObjectImpl.prototype;
58198      * e.keyCode = e.TAB
58199      *
58200      * 
58201      */
58202     onEditorKey : function(field, e){
58203         
58204         var k = e.getKey(),
58205             newCell,
58206             g = this.grid,
58207             ed = g.activeEditor,
58208             forward = false;
58209         ///Roo.log('onEditorKey' + k);
58210         
58211         
58212         if (this.enter_is_tab && k == e.ENTER) {
58213             k = e.TAB;
58214         }
58215         
58216         if(k == e.TAB){
58217             if(e.shiftKey){
58218                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58219             }else{
58220                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58221                 forward = true;
58222             }
58223             
58224             e.stopEvent();
58225             
58226         } else if(k == e.ENTER &&  !e.ctrlKey){
58227             ed.completeEdit();
58228             e.stopEvent();
58229             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58230         
58231                 } else if(k == e.ESC){
58232             ed.cancelEdit();
58233         }
58234                 
58235         if (newCell) {
58236             var ecall = { cell : newCell, forward : forward };
58237             this.fireEvent('beforeeditnext', ecall );
58238             newCell = ecall.cell;
58239                         forward = ecall.forward;
58240         }
58241                 
58242         if(newCell){
58243             //Roo.log('next cell after edit');
58244             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58245         } else if (forward) {
58246             // tabbed past last
58247             this.fireEvent.defer(100, this, ['tabend',this]);
58248         }
58249     }
58250 });/*
58251  * Based on:
58252  * Ext JS Library 1.1.1
58253  * Copyright(c) 2006-2007, Ext JS, LLC.
58254  *
58255  * Originally Released Under LGPL - original licence link has changed is not relivant.
58256  *
58257  * Fork - LGPL
58258  * <script type="text/javascript">
58259  */
58260  
58261 /**
58262  * @class Roo.grid.EditorGrid
58263  * @extends Roo.grid.Grid
58264  * Class for creating and editable grid.
58265  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58266  * The container MUST have some type of size defined for the grid to fill. The container will be 
58267  * automatically set to position relative if it isn't already.
58268  * @param {Object} dataSource The data model to bind to
58269  * @param {Object} colModel The column model with info about this grid's columns
58270  */
58271 Roo.grid.EditorGrid = function(container, config){
58272     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58273     this.getGridEl().addClass("xedit-grid");
58274
58275     if(!this.selModel){
58276         this.selModel = new Roo.grid.CellSelectionModel();
58277     }
58278
58279     this.activeEditor = null;
58280
58281         this.addEvents({
58282             /**
58283              * @event beforeedit
58284              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58285              * <ul style="padding:5px;padding-left:16px;">
58286              * <li>grid - This grid</li>
58287              * <li>record - The record being edited</li>
58288              * <li>field - The field name being edited</li>
58289              * <li>value - The value for the field being edited.</li>
58290              * <li>row - The grid row index</li>
58291              * <li>column - The grid column index</li>
58292              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58293              * </ul>
58294              * @param {Object} e An edit event (see above for description)
58295              */
58296             "beforeedit" : true,
58297             /**
58298              * @event afteredit
58299              * Fires after a cell is edited. <br />
58300              * <ul style="padding:5px;padding-left:16px;">
58301              * <li>grid - This grid</li>
58302              * <li>record - The record being edited</li>
58303              * <li>field - The field name being edited</li>
58304              * <li>value - The value being set</li>
58305              * <li>originalValue - The original value for the field, before the edit.</li>
58306              * <li>row - The grid row index</li>
58307              * <li>column - The grid column index</li>
58308              * </ul>
58309              * @param {Object} e An edit event (see above for description)
58310              */
58311             "afteredit" : true,
58312             /**
58313              * @event validateedit
58314              * Fires after a cell is edited, but before the value is set in the record. 
58315          * You can use this to modify the value being set in the field, Return false
58316              * to cancel the change. The edit event object has the following properties <br />
58317              * <ul style="padding:5px;padding-left:16px;">
58318          * <li>editor - This editor</li>
58319              * <li>grid - This grid</li>
58320              * <li>record - The record being edited</li>
58321              * <li>field - The field name being edited</li>
58322              * <li>value - The value being set</li>
58323              * <li>originalValue - The original value for the field, before the edit.</li>
58324              * <li>row - The grid row index</li>
58325              * <li>column - The grid column index</li>
58326              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58327              * </ul>
58328              * @param {Object} e An edit event (see above for description)
58329              */
58330             "validateedit" : true
58331         });
58332     this.on("bodyscroll", this.stopEditing,  this);
58333     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58334 };
58335
58336 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58337     /**
58338      * @cfg {Number} clicksToEdit
58339      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58340      */
58341     clicksToEdit: 2,
58342
58343     // private
58344     isEditor : true,
58345     // private
58346     trackMouseOver: false, // causes very odd FF errors
58347
58348     onCellDblClick : function(g, row, col){
58349         this.startEditing(row, col);
58350     },
58351
58352     onEditComplete : function(ed, value, startValue){
58353         this.editing = false;
58354         this.activeEditor = null;
58355         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58356         var r = ed.record;
58357         var field = this.colModel.getDataIndex(ed.col);
58358         var e = {
58359             grid: this,
58360             record: r,
58361             field: field,
58362             originalValue: startValue,
58363             value: value,
58364             row: ed.row,
58365             column: ed.col,
58366             cancel:false,
58367             editor: ed
58368         };
58369         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58370         cell.show();
58371           
58372         if(String(value) !== String(startValue)){
58373             
58374             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58375                 r.set(field, e.value);
58376                 // if we are dealing with a combo box..
58377                 // then we also set the 'name' colum to be the displayField
58378                 if (ed.field.displayField && ed.field.name) {
58379                     r.set(ed.field.name, ed.field.el.dom.value);
58380                 }
58381                 
58382                 delete e.cancel; //?? why!!!
58383                 this.fireEvent("afteredit", e);
58384             }
58385         } else {
58386             this.fireEvent("afteredit", e); // always fire it!
58387         }
58388         this.view.focusCell(ed.row, ed.col);
58389     },
58390
58391     /**
58392      * Starts editing the specified for the specified row/column
58393      * @param {Number} rowIndex
58394      * @param {Number} colIndex
58395      */
58396     startEditing : function(row, col){
58397         this.stopEditing();
58398         if(this.colModel.isCellEditable(col, row)){
58399             this.view.ensureVisible(row, col, true);
58400           
58401             var r = this.dataSource.getAt(row);
58402             var field = this.colModel.getDataIndex(col);
58403             var cell = Roo.get(this.view.getCell(row,col));
58404             var e = {
58405                 grid: this,
58406                 record: r,
58407                 field: field,
58408                 value: r.data[field],
58409                 row: row,
58410                 column: col,
58411                 cancel:false 
58412             };
58413             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58414                 this.editing = true;
58415                 var ed = this.colModel.getCellEditor(col, row);
58416                 
58417                 if (!ed) {
58418                     return;
58419                 }
58420                 if(!ed.rendered){
58421                     ed.render(ed.parentEl || document.body);
58422                 }
58423                 ed.field.reset();
58424                
58425                 cell.hide();
58426                 
58427                 (function(){ // complex but required for focus issues in safari, ie and opera
58428                     ed.row = row;
58429                     ed.col = col;
58430                     ed.record = r;
58431                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58432                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58433                     this.activeEditor = ed;
58434                     var v = r.data[field];
58435                     ed.startEdit(this.view.getCell(row, col), v);
58436                     // combo's with 'displayField and name set
58437                     if (ed.field.displayField && ed.field.name) {
58438                         ed.field.el.dom.value = r.data[ed.field.name];
58439                     }
58440                     
58441                     
58442                 }).defer(50, this);
58443             }
58444         }
58445     },
58446         
58447     /**
58448      * Stops any active editing
58449      */
58450     stopEditing : function(){
58451         if(this.activeEditor){
58452             this.activeEditor.completeEdit();
58453         }
58454         this.activeEditor = null;
58455     },
58456         
58457          /**
58458      * Called to get grid's drag proxy text, by default returns this.ddText.
58459      * @return {String}
58460      */
58461     getDragDropText : function(){
58462         var count = this.selModel.getSelectedCell() ? 1 : 0;
58463         return String.format(this.ddText, count, count == 1 ? '' : 's');
58464     }
58465         
58466 });/*
58467  * Based on:
58468  * Ext JS Library 1.1.1
58469  * Copyright(c) 2006-2007, Ext JS, LLC.
58470  *
58471  * Originally Released Under LGPL - original licence link has changed is not relivant.
58472  *
58473  * Fork - LGPL
58474  * <script type="text/javascript">
58475  */
58476
58477 // private - not really -- you end up using it !
58478 // This is a support class used internally by the Grid components
58479
58480 /**
58481  * @class Roo.grid.GridEditor
58482  * @extends Roo.Editor
58483  * Class for creating and editable grid elements.
58484  * @param {Object} config any settings (must include field)
58485  */
58486 Roo.grid.GridEditor = function(field, config){
58487     if (!config && field.field) {
58488         config = field;
58489         field = Roo.factory(config.field, Roo.form);
58490     }
58491     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58492     field.monitorTab = false;
58493 };
58494
58495 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58496     
58497     /**
58498      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58499      */
58500     
58501     alignment: "tl-tl",
58502     autoSize: "width",
58503     hideEl : false,
58504     cls: "x-small-editor x-grid-editor",
58505     shim:false,
58506     shadow:"frame"
58507 });/*
58508  * Based on:
58509  * Ext JS Library 1.1.1
58510  * Copyright(c) 2006-2007, Ext JS, LLC.
58511  *
58512  * Originally Released Under LGPL - original licence link has changed is not relivant.
58513  *
58514  * Fork - LGPL
58515  * <script type="text/javascript">
58516  */
58517   
58518
58519   
58520 Roo.grid.PropertyRecord = Roo.data.Record.create([
58521     {name:'name',type:'string'},  'value'
58522 ]);
58523
58524
58525 Roo.grid.PropertyStore = function(grid, source){
58526     this.grid = grid;
58527     this.store = new Roo.data.Store({
58528         recordType : Roo.grid.PropertyRecord
58529     });
58530     this.store.on('update', this.onUpdate,  this);
58531     if(source){
58532         this.setSource(source);
58533     }
58534     Roo.grid.PropertyStore.superclass.constructor.call(this);
58535 };
58536
58537
58538
58539 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58540     setSource : function(o){
58541         this.source = o;
58542         this.store.removeAll();
58543         var data = [];
58544         for(var k in o){
58545             if(this.isEditableValue(o[k])){
58546                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58547             }
58548         }
58549         this.store.loadRecords({records: data}, {}, true);
58550     },
58551
58552     onUpdate : function(ds, record, type){
58553         if(type == Roo.data.Record.EDIT){
58554             var v = record.data['value'];
58555             var oldValue = record.modified['value'];
58556             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58557                 this.source[record.id] = v;
58558                 record.commit();
58559                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58560             }else{
58561                 record.reject();
58562             }
58563         }
58564     },
58565
58566     getProperty : function(row){
58567        return this.store.getAt(row);
58568     },
58569
58570     isEditableValue: function(val){
58571         if(val && val instanceof Date){
58572             return true;
58573         }else if(typeof val == 'object' || typeof val == 'function'){
58574             return false;
58575         }
58576         return true;
58577     },
58578
58579     setValue : function(prop, value){
58580         this.source[prop] = value;
58581         this.store.getById(prop).set('value', value);
58582     },
58583
58584     getSource : function(){
58585         return this.source;
58586     }
58587 });
58588
58589 Roo.grid.PropertyColumnModel = function(grid, store){
58590     this.grid = grid;
58591     var g = Roo.grid;
58592     g.PropertyColumnModel.superclass.constructor.call(this, [
58593         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58594         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58595     ]);
58596     this.store = store;
58597     this.bselect = Roo.DomHelper.append(document.body, {
58598         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58599             {tag: 'option', value: 'true', html: 'true'},
58600             {tag: 'option', value: 'false', html: 'false'}
58601         ]
58602     });
58603     Roo.id(this.bselect);
58604     var f = Roo.form;
58605     this.editors = {
58606         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58607         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58608         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58609         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58610         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58611     };
58612     this.renderCellDelegate = this.renderCell.createDelegate(this);
58613     this.renderPropDelegate = this.renderProp.createDelegate(this);
58614 };
58615
58616 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58617     
58618     
58619     nameText : 'Name',
58620     valueText : 'Value',
58621     
58622     dateFormat : 'm/j/Y',
58623     
58624     
58625     renderDate : function(dateVal){
58626         return dateVal.dateFormat(this.dateFormat);
58627     },
58628
58629     renderBool : function(bVal){
58630         return bVal ? 'true' : 'false';
58631     },
58632
58633     isCellEditable : function(colIndex, rowIndex){
58634         return colIndex == 1;
58635     },
58636
58637     getRenderer : function(col){
58638         return col == 1 ?
58639             this.renderCellDelegate : this.renderPropDelegate;
58640     },
58641
58642     renderProp : function(v){
58643         return this.getPropertyName(v);
58644     },
58645
58646     renderCell : function(val){
58647         var rv = val;
58648         if(val instanceof Date){
58649             rv = this.renderDate(val);
58650         }else if(typeof val == 'boolean'){
58651             rv = this.renderBool(val);
58652         }
58653         return Roo.util.Format.htmlEncode(rv);
58654     },
58655
58656     getPropertyName : function(name){
58657         var pn = this.grid.propertyNames;
58658         return pn && pn[name] ? pn[name] : name;
58659     },
58660
58661     getCellEditor : function(colIndex, rowIndex){
58662         var p = this.store.getProperty(rowIndex);
58663         var n = p.data['name'], val = p.data['value'];
58664         
58665         if(typeof(this.grid.customEditors[n]) == 'string'){
58666             return this.editors[this.grid.customEditors[n]];
58667         }
58668         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58669             return this.grid.customEditors[n];
58670         }
58671         if(val instanceof Date){
58672             return this.editors['date'];
58673         }else if(typeof val == 'number'){
58674             return this.editors['number'];
58675         }else if(typeof val == 'boolean'){
58676             return this.editors['boolean'];
58677         }else{
58678             return this.editors['string'];
58679         }
58680     }
58681 });
58682
58683 /**
58684  * @class Roo.grid.PropertyGrid
58685  * @extends Roo.grid.EditorGrid
58686  * This class represents the  interface of a component based property grid control.
58687  * <br><br>Usage:<pre><code>
58688  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58689       
58690  });
58691  // set any options
58692  grid.render();
58693  * </code></pre>
58694   
58695  * @constructor
58696  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58697  * The container MUST have some type of size defined for the grid to fill. The container will be
58698  * automatically set to position relative if it isn't already.
58699  * @param {Object} config A config object that sets properties on this grid.
58700  */
58701 Roo.grid.PropertyGrid = function(container, config){
58702     config = config || {};
58703     var store = new Roo.grid.PropertyStore(this);
58704     this.store = store;
58705     var cm = new Roo.grid.PropertyColumnModel(this, store);
58706     store.store.sort('name', 'ASC');
58707     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58708         ds: store.store,
58709         cm: cm,
58710         enableColLock:false,
58711         enableColumnMove:false,
58712         stripeRows:false,
58713         trackMouseOver: false,
58714         clicksToEdit:1
58715     }, config));
58716     this.getGridEl().addClass('x-props-grid');
58717     this.lastEditRow = null;
58718     this.on('columnresize', this.onColumnResize, this);
58719     this.addEvents({
58720          /**
58721              * @event beforepropertychange
58722              * Fires before a property changes (return false to stop?)
58723              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58724              * @param {String} id Record Id
58725              * @param {String} newval New Value
58726          * @param {String} oldval Old Value
58727              */
58728         "beforepropertychange": true,
58729         /**
58730              * @event propertychange
58731              * Fires after a property changes
58732              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58733              * @param {String} id Record Id
58734              * @param {String} newval New Value
58735          * @param {String} oldval Old Value
58736              */
58737         "propertychange": true
58738     });
58739     this.customEditors = this.customEditors || {};
58740 };
58741 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58742     
58743      /**
58744      * @cfg {Object} customEditors map of colnames=> custom editors.
58745      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58746      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58747      * false disables editing of the field.
58748          */
58749     
58750       /**
58751      * @cfg {Object} propertyNames map of property Names to their displayed value
58752          */
58753     
58754     render : function(){
58755         Roo.grid.PropertyGrid.superclass.render.call(this);
58756         this.autoSize.defer(100, this);
58757     },
58758
58759     autoSize : function(){
58760         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58761         if(this.view){
58762             this.view.fitColumns();
58763         }
58764     },
58765
58766     onColumnResize : function(){
58767         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58768         this.autoSize();
58769     },
58770     /**
58771      * Sets the data for the Grid
58772      * accepts a Key => Value object of all the elements avaiable.
58773      * @param {Object} data  to appear in grid.
58774      */
58775     setSource : function(source){
58776         this.store.setSource(source);
58777         //this.autoSize();
58778     },
58779     /**
58780      * Gets all the data from the grid.
58781      * @return {Object} data  data stored in grid
58782      */
58783     getSource : function(){
58784         return this.store.getSource();
58785     }
58786 });/*
58787   
58788  * Licence LGPL
58789  
58790  */
58791  
58792 /**
58793  * @class Roo.grid.Calendar
58794  * @extends Roo.util.Grid
58795  * This class extends the Grid to provide a calendar widget
58796  * <br><br>Usage:<pre><code>
58797  var grid = new Roo.grid.Calendar("my-container-id", {
58798      ds: myDataStore,
58799      cm: myColModel,
58800      selModel: mySelectionModel,
58801      autoSizeColumns: true,
58802      monitorWindowResize: false,
58803      trackMouseOver: true
58804      eventstore : real data store..
58805  });
58806  // set any options
58807  grid.render();
58808   
58809   * @constructor
58810  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58811  * The container MUST have some type of size defined for the grid to fill. The container will be
58812  * automatically set to position relative if it isn't already.
58813  * @param {Object} config A config object that sets properties on this grid.
58814  */
58815 Roo.grid.Calendar = function(container, config){
58816         // initialize the container
58817         this.container = Roo.get(container);
58818         this.container.update("");
58819         this.container.setStyle("overflow", "hidden");
58820     this.container.addClass('x-grid-container');
58821
58822     this.id = this.container.id;
58823
58824     Roo.apply(this, config);
58825     // check and correct shorthanded configs
58826     
58827     var rows = [];
58828     var d =1;
58829     for (var r = 0;r < 6;r++) {
58830         
58831         rows[r]=[];
58832         for (var c =0;c < 7;c++) {
58833             rows[r][c]= '';
58834         }
58835     }
58836     if (this.eventStore) {
58837         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58838         this.eventStore.on('load',this.onLoad, this);
58839         this.eventStore.on('beforeload',this.clearEvents, this);
58840          
58841     }
58842     
58843     this.dataSource = new Roo.data.Store({
58844             proxy: new Roo.data.MemoryProxy(rows),
58845             reader: new Roo.data.ArrayReader({}, [
58846                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58847     });
58848
58849     this.dataSource.load();
58850     this.ds = this.dataSource;
58851     this.ds.xmodule = this.xmodule || false;
58852     
58853     
58854     var cellRender = function(v,x,r)
58855     {
58856         return String.format(
58857             '<div class="fc-day  fc-widget-content"><div>' +
58858                 '<div class="fc-event-container"></div>' +
58859                 '<div class="fc-day-number">{0}</div>'+
58860                 
58861                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58862             '</div></div>', v);
58863     
58864     }
58865     
58866     
58867     this.colModel = new Roo.grid.ColumnModel( [
58868         {
58869             xtype: 'ColumnModel',
58870             xns: Roo.grid,
58871             dataIndex : 'weekday0',
58872             header : 'Sunday',
58873             renderer : cellRender
58874         },
58875         {
58876             xtype: 'ColumnModel',
58877             xns: Roo.grid,
58878             dataIndex : 'weekday1',
58879             header : 'Monday',
58880             renderer : cellRender
58881         },
58882         {
58883             xtype: 'ColumnModel',
58884             xns: Roo.grid,
58885             dataIndex : 'weekday2',
58886             header : 'Tuesday',
58887             renderer : cellRender
58888         },
58889         {
58890             xtype: 'ColumnModel',
58891             xns: Roo.grid,
58892             dataIndex : 'weekday3',
58893             header : 'Wednesday',
58894             renderer : cellRender
58895         },
58896         {
58897             xtype: 'ColumnModel',
58898             xns: Roo.grid,
58899             dataIndex : 'weekday4',
58900             header : 'Thursday',
58901             renderer : cellRender
58902         },
58903         {
58904             xtype: 'ColumnModel',
58905             xns: Roo.grid,
58906             dataIndex : 'weekday5',
58907             header : 'Friday',
58908             renderer : cellRender
58909         },
58910         {
58911             xtype: 'ColumnModel',
58912             xns: Roo.grid,
58913             dataIndex : 'weekday6',
58914             header : 'Saturday',
58915             renderer : cellRender
58916         }
58917     ]);
58918     this.cm = this.colModel;
58919     this.cm.xmodule = this.xmodule || false;
58920  
58921         
58922           
58923     //this.selModel = new Roo.grid.CellSelectionModel();
58924     //this.sm = this.selModel;
58925     //this.selModel.init(this);
58926     
58927     
58928     if(this.width){
58929         this.container.setWidth(this.width);
58930     }
58931
58932     if(this.height){
58933         this.container.setHeight(this.height);
58934     }
58935     /** @private */
58936         this.addEvents({
58937         // raw events
58938         /**
58939          * @event click
58940          * The raw click event for the entire grid.
58941          * @param {Roo.EventObject} e
58942          */
58943         "click" : true,
58944         /**
58945          * @event dblclick
58946          * The raw dblclick event for the entire grid.
58947          * @param {Roo.EventObject} e
58948          */
58949         "dblclick" : true,
58950         /**
58951          * @event contextmenu
58952          * The raw contextmenu event for the entire grid.
58953          * @param {Roo.EventObject} e
58954          */
58955         "contextmenu" : true,
58956         /**
58957          * @event mousedown
58958          * The raw mousedown event for the entire grid.
58959          * @param {Roo.EventObject} e
58960          */
58961         "mousedown" : true,
58962         /**
58963          * @event mouseup
58964          * The raw mouseup event for the entire grid.
58965          * @param {Roo.EventObject} e
58966          */
58967         "mouseup" : true,
58968         /**
58969          * @event mouseover
58970          * The raw mouseover event for the entire grid.
58971          * @param {Roo.EventObject} e
58972          */
58973         "mouseover" : true,
58974         /**
58975          * @event mouseout
58976          * The raw mouseout event for the entire grid.
58977          * @param {Roo.EventObject} e
58978          */
58979         "mouseout" : true,
58980         /**
58981          * @event keypress
58982          * The raw keypress event for the entire grid.
58983          * @param {Roo.EventObject} e
58984          */
58985         "keypress" : true,
58986         /**
58987          * @event keydown
58988          * The raw keydown event for the entire grid.
58989          * @param {Roo.EventObject} e
58990          */
58991         "keydown" : true,
58992
58993         // custom events
58994
58995         /**
58996          * @event cellclick
58997          * Fires when a cell is clicked
58998          * @param {Grid} this
58999          * @param {Number} rowIndex
59000          * @param {Number} columnIndex
59001          * @param {Roo.EventObject} e
59002          */
59003         "cellclick" : true,
59004         /**
59005          * @event celldblclick
59006          * Fires when a cell is double clicked
59007          * @param {Grid} this
59008          * @param {Number} rowIndex
59009          * @param {Number} columnIndex
59010          * @param {Roo.EventObject} e
59011          */
59012         "celldblclick" : true,
59013         /**
59014          * @event rowclick
59015          * Fires when a row is clicked
59016          * @param {Grid} this
59017          * @param {Number} rowIndex
59018          * @param {Roo.EventObject} e
59019          */
59020         "rowclick" : true,
59021         /**
59022          * @event rowdblclick
59023          * Fires when a row is double clicked
59024          * @param {Grid} this
59025          * @param {Number} rowIndex
59026          * @param {Roo.EventObject} e
59027          */
59028         "rowdblclick" : true,
59029         /**
59030          * @event headerclick
59031          * Fires when a header is clicked
59032          * @param {Grid} this
59033          * @param {Number} columnIndex
59034          * @param {Roo.EventObject} e
59035          */
59036         "headerclick" : true,
59037         /**
59038          * @event headerdblclick
59039          * Fires when a header cell is double clicked
59040          * @param {Grid} this
59041          * @param {Number} columnIndex
59042          * @param {Roo.EventObject} e
59043          */
59044         "headerdblclick" : true,
59045         /**
59046          * @event rowcontextmenu
59047          * Fires when a row is right clicked
59048          * @param {Grid} this
59049          * @param {Number} rowIndex
59050          * @param {Roo.EventObject} e
59051          */
59052         "rowcontextmenu" : true,
59053         /**
59054          * @event cellcontextmenu
59055          * Fires when a cell is right clicked
59056          * @param {Grid} this
59057          * @param {Number} rowIndex
59058          * @param {Number} cellIndex
59059          * @param {Roo.EventObject} e
59060          */
59061          "cellcontextmenu" : true,
59062         /**
59063          * @event headercontextmenu
59064          * Fires when a header is right clicked
59065          * @param {Grid} this
59066          * @param {Number} columnIndex
59067          * @param {Roo.EventObject} e
59068          */
59069         "headercontextmenu" : true,
59070         /**
59071          * @event bodyscroll
59072          * Fires when the body element is scrolled
59073          * @param {Number} scrollLeft
59074          * @param {Number} scrollTop
59075          */
59076         "bodyscroll" : true,
59077         /**
59078          * @event columnresize
59079          * Fires when the user resizes a column
59080          * @param {Number} columnIndex
59081          * @param {Number} newSize
59082          */
59083         "columnresize" : true,
59084         /**
59085          * @event columnmove
59086          * Fires when the user moves a column
59087          * @param {Number} oldIndex
59088          * @param {Number} newIndex
59089          */
59090         "columnmove" : true,
59091         /**
59092          * @event startdrag
59093          * Fires when row(s) start being dragged
59094          * @param {Grid} this
59095          * @param {Roo.GridDD} dd The drag drop object
59096          * @param {event} e The raw browser event
59097          */
59098         "startdrag" : true,
59099         /**
59100          * @event enddrag
59101          * Fires when a drag operation is complete
59102          * @param {Grid} this
59103          * @param {Roo.GridDD} dd The drag drop object
59104          * @param {event} e The raw browser event
59105          */
59106         "enddrag" : true,
59107         /**
59108          * @event dragdrop
59109          * Fires when dragged row(s) are dropped on a valid DD target
59110          * @param {Grid} this
59111          * @param {Roo.GridDD} dd The drag drop object
59112          * @param {String} targetId The target drag drop object
59113          * @param {event} e The raw browser event
59114          */
59115         "dragdrop" : true,
59116         /**
59117          * @event dragover
59118          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59119          * @param {Grid} this
59120          * @param {Roo.GridDD} dd The drag drop object
59121          * @param {String} targetId The target drag drop object
59122          * @param {event} e The raw browser event
59123          */
59124         "dragover" : true,
59125         /**
59126          * @event dragenter
59127          *  Fires when the dragged row(s) first cross another DD target while being dragged
59128          * @param {Grid} this
59129          * @param {Roo.GridDD} dd The drag drop object
59130          * @param {String} targetId The target drag drop object
59131          * @param {event} e The raw browser event
59132          */
59133         "dragenter" : true,
59134         /**
59135          * @event dragout
59136          * Fires when the dragged row(s) leave another DD target while being dragged
59137          * @param {Grid} this
59138          * @param {Roo.GridDD} dd The drag drop object
59139          * @param {String} targetId The target drag drop object
59140          * @param {event} e The raw browser event
59141          */
59142         "dragout" : true,
59143         /**
59144          * @event rowclass
59145          * Fires when a row is rendered, so you can change add a style to it.
59146          * @param {GridView} gridview   The grid view
59147          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59148          */
59149         'rowclass' : true,
59150
59151         /**
59152          * @event render
59153          * Fires when the grid is rendered
59154          * @param {Grid} grid
59155          */
59156         'render' : true,
59157             /**
59158              * @event select
59159              * Fires when a date is selected
59160              * @param {DatePicker} this
59161              * @param {Date} date The selected date
59162              */
59163         'select': true,
59164         /**
59165              * @event monthchange
59166              * Fires when the displayed month changes 
59167              * @param {DatePicker} this
59168              * @param {Date} date The selected month
59169              */
59170         'monthchange': true,
59171         /**
59172              * @event evententer
59173              * Fires when mouse over an event
59174              * @param {Calendar} this
59175              * @param {event} Event
59176              */
59177         'evententer': true,
59178         /**
59179              * @event eventleave
59180              * Fires when the mouse leaves an
59181              * @param {Calendar} this
59182              * @param {event}
59183              */
59184         'eventleave': true,
59185         /**
59186              * @event eventclick
59187              * Fires when the mouse click an
59188              * @param {Calendar} this
59189              * @param {event}
59190              */
59191         'eventclick': true,
59192         /**
59193              * @event eventrender
59194              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59195              * @param {Calendar} this
59196              * @param {data} data to be modified
59197              */
59198         'eventrender': true
59199         
59200     });
59201
59202     Roo.grid.Grid.superclass.constructor.call(this);
59203     this.on('render', function() {
59204         this.view.el.addClass('x-grid-cal'); 
59205         
59206         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59207
59208     },this);
59209     
59210     if (!Roo.grid.Calendar.style) {
59211         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59212             
59213             
59214             '.x-grid-cal .x-grid-col' :  {
59215                 height: 'auto !important',
59216                 'vertical-align': 'top'
59217             },
59218             '.x-grid-cal  .fc-event-hori' : {
59219                 height: '14px'
59220             }
59221              
59222             
59223         }, Roo.id());
59224     }
59225
59226     
59227     
59228 };
59229 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59230     /**
59231      * @cfg {Store} eventStore The store that loads events.
59232      */
59233     eventStore : 25,
59234
59235      
59236     activeDate : false,
59237     startDay : 0,
59238     autoWidth : true,
59239     monitorWindowResize : false,
59240
59241     
59242     resizeColumns : function() {
59243         var col = (this.view.el.getWidth() / 7) - 3;
59244         // loop through cols, and setWidth
59245         for(var i =0 ; i < 7 ; i++){
59246             this.cm.setColumnWidth(i, col);
59247         }
59248     },
59249      setDate :function(date) {
59250         
59251         Roo.log('setDate?');
59252         
59253         this.resizeColumns();
59254         var vd = this.activeDate;
59255         this.activeDate = date;
59256 //        if(vd && this.el){
59257 //            var t = date.getTime();
59258 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59259 //                Roo.log('using add remove');
59260 //                
59261 //                this.fireEvent('monthchange', this, date);
59262 //                
59263 //                this.cells.removeClass("fc-state-highlight");
59264 //                this.cells.each(function(c){
59265 //                   if(c.dateValue == t){
59266 //                       c.addClass("fc-state-highlight");
59267 //                       setTimeout(function(){
59268 //                            try{c.dom.firstChild.focus();}catch(e){}
59269 //                       }, 50);
59270 //                       return false;
59271 //                   }
59272 //                   return true;
59273 //                });
59274 //                return;
59275 //            }
59276 //        }
59277         
59278         var days = date.getDaysInMonth();
59279         
59280         var firstOfMonth = date.getFirstDateOfMonth();
59281         var startingPos = firstOfMonth.getDay()-this.startDay;
59282         
59283         if(startingPos < this.startDay){
59284             startingPos += 7;
59285         }
59286         
59287         var pm = date.add(Date.MONTH, -1);
59288         var prevStart = pm.getDaysInMonth()-startingPos;
59289 //        
59290         
59291         
59292         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59293         
59294         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59295         //this.cells.addClassOnOver('fc-state-hover');
59296         
59297         var cells = this.cells.elements;
59298         var textEls = this.textNodes;
59299         
59300         //Roo.each(cells, function(cell){
59301         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59302         //});
59303         
59304         days += startingPos;
59305
59306         // convert everything to numbers so it's fast
59307         var day = 86400000;
59308         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59309         //Roo.log(d);
59310         //Roo.log(pm);
59311         //Roo.log(prevStart);
59312         
59313         var today = new Date().clearTime().getTime();
59314         var sel = date.clearTime().getTime();
59315         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59316         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59317         var ddMatch = this.disabledDatesRE;
59318         var ddText = this.disabledDatesText;
59319         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59320         var ddaysText = this.disabledDaysText;
59321         var format = this.format;
59322         
59323         var setCellClass = function(cal, cell){
59324             
59325             //Roo.log('set Cell Class');
59326             cell.title = "";
59327             var t = d.getTime();
59328             
59329             //Roo.log(d);
59330             
59331             
59332             cell.dateValue = t;
59333             if(t == today){
59334                 cell.className += " fc-today";
59335                 cell.className += " fc-state-highlight";
59336                 cell.title = cal.todayText;
59337             }
59338             if(t == sel){
59339                 // disable highlight in other month..
59340                 cell.className += " fc-state-highlight";
59341                 
59342             }
59343             // disabling
59344             if(t < min) {
59345                 //cell.className = " fc-state-disabled";
59346                 cell.title = cal.minText;
59347                 return;
59348             }
59349             if(t > max) {
59350                 //cell.className = " fc-state-disabled";
59351                 cell.title = cal.maxText;
59352                 return;
59353             }
59354             if(ddays){
59355                 if(ddays.indexOf(d.getDay()) != -1){
59356                     // cell.title = ddaysText;
59357                    // cell.className = " fc-state-disabled";
59358                 }
59359             }
59360             if(ddMatch && format){
59361                 var fvalue = d.dateFormat(format);
59362                 if(ddMatch.test(fvalue)){
59363                     cell.title = ddText.replace("%0", fvalue);
59364                    cell.className = " fc-state-disabled";
59365                 }
59366             }
59367             
59368             if (!cell.initialClassName) {
59369                 cell.initialClassName = cell.dom.className;
59370             }
59371             
59372             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59373         };
59374
59375         var i = 0;
59376         
59377         for(; i < startingPos; i++) {
59378             cells[i].dayName =  (++prevStart);
59379             Roo.log(textEls[i]);
59380             d.setDate(d.getDate()+1);
59381             
59382             //cells[i].className = "fc-past fc-other-month";
59383             setCellClass(this, cells[i]);
59384         }
59385         
59386         var intDay = 0;
59387         
59388         for(; i < days; i++){
59389             intDay = i - startingPos + 1;
59390             cells[i].dayName =  (intDay);
59391             d.setDate(d.getDate()+1);
59392             
59393             cells[i].className = ''; // "x-date-active";
59394             setCellClass(this, cells[i]);
59395         }
59396         var extraDays = 0;
59397         
59398         for(; i < 42; i++) {
59399             //textEls[i].innerHTML = (++extraDays);
59400             
59401             d.setDate(d.getDate()+1);
59402             cells[i].dayName = (++extraDays);
59403             cells[i].className = "fc-future fc-other-month";
59404             setCellClass(this, cells[i]);
59405         }
59406         
59407         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59408         
59409         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59410         
59411         // this will cause all the cells to mis
59412         var rows= [];
59413         var i =0;
59414         for (var r = 0;r < 6;r++) {
59415             for (var c =0;c < 7;c++) {
59416                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59417             }    
59418         }
59419         
59420         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59421         for(i=0;i<cells.length;i++) {
59422             
59423             this.cells.elements[i].dayName = cells[i].dayName ;
59424             this.cells.elements[i].className = cells[i].className;
59425             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59426             this.cells.elements[i].title = cells[i].title ;
59427             this.cells.elements[i].dateValue = cells[i].dateValue ;
59428         }
59429         
59430         
59431         
59432         
59433         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59434         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59435         
59436         ////if(totalRows != 6){
59437             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59438            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59439        // }
59440         
59441         this.fireEvent('monthchange', this, date);
59442         
59443         
59444     },
59445  /**
59446      * Returns the grid's SelectionModel.
59447      * @return {SelectionModel}
59448      */
59449     getSelectionModel : function(){
59450         if(!this.selModel){
59451             this.selModel = new Roo.grid.CellSelectionModel();
59452         }
59453         return this.selModel;
59454     },
59455
59456     load: function() {
59457         this.eventStore.load()
59458         
59459         
59460         
59461     },
59462     
59463     findCell : function(dt) {
59464         dt = dt.clearTime().getTime();
59465         var ret = false;
59466         this.cells.each(function(c){
59467             //Roo.log("check " +c.dateValue + '?=' + dt);
59468             if(c.dateValue == dt){
59469                 ret = c;
59470                 return false;
59471             }
59472             return true;
59473         });
59474         
59475         return ret;
59476     },
59477     
59478     findCells : function(rec) {
59479         var s = rec.data.start_dt.clone().clearTime().getTime();
59480        // Roo.log(s);
59481         var e= rec.data.end_dt.clone().clearTime().getTime();
59482        // Roo.log(e);
59483         var ret = [];
59484         this.cells.each(function(c){
59485              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59486             
59487             if(c.dateValue > e){
59488                 return ;
59489             }
59490             if(c.dateValue < s){
59491                 return ;
59492             }
59493             ret.push(c);
59494         });
59495         
59496         return ret;    
59497     },
59498     
59499     findBestRow: function(cells)
59500     {
59501         var ret = 0;
59502         
59503         for (var i =0 ; i < cells.length;i++) {
59504             ret  = Math.max(cells[i].rows || 0,ret);
59505         }
59506         return ret;
59507         
59508     },
59509     
59510     
59511     addItem : function(rec)
59512     {
59513         // look for vertical location slot in
59514         var cells = this.findCells(rec);
59515         
59516         rec.row = this.findBestRow(cells);
59517         
59518         // work out the location.
59519         
59520         var crow = false;
59521         var rows = [];
59522         for(var i =0; i < cells.length; i++) {
59523             if (!crow) {
59524                 crow = {
59525                     start : cells[i],
59526                     end :  cells[i]
59527                 };
59528                 continue;
59529             }
59530             if (crow.start.getY() == cells[i].getY()) {
59531                 // on same row.
59532                 crow.end = cells[i];
59533                 continue;
59534             }
59535             // different row.
59536             rows.push(crow);
59537             crow = {
59538                 start: cells[i],
59539                 end : cells[i]
59540             };
59541             
59542         }
59543         
59544         rows.push(crow);
59545         rec.els = [];
59546         rec.rows = rows;
59547         rec.cells = cells;
59548         for (var i = 0; i < cells.length;i++) {
59549             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59550             
59551         }
59552         
59553         
59554     },
59555     
59556     clearEvents: function() {
59557         
59558         if (!this.eventStore.getCount()) {
59559             return;
59560         }
59561         // reset number of rows in cells.
59562         Roo.each(this.cells.elements, function(c){
59563             c.rows = 0;
59564         });
59565         
59566         this.eventStore.each(function(e) {
59567             this.clearEvent(e);
59568         },this);
59569         
59570     },
59571     
59572     clearEvent : function(ev)
59573     {
59574         if (ev.els) {
59575             Roo.each(ev.els, function(el) {
59576                 el.un('mouseenter' ,this.onEventEnter, this);
59577                 el.un('mouseleave' ,this.onEventLeave, this);
59578                 el.remove();
59579             },this);
59580             ev.els = [];
59581         }
59582     },
59583     
59584     
59585     renderEvent : function(ev,ctr) {
59586         if (!ctr) {
59587              ctr = this.view.el.select('.fc-event-container',true).first();
59588         }
59589         
59590          
59591         this.clearEvent(ev);
59592             //code
59593        
59594         
59595         
59596         ev.els = [];
59597         var cells = ev.cells;
59598         var rows = ev.rows;
59599         this.fireEvent('eventrender', this, ev);
59600         
59601         for(var i =0; i < rows.length; i++) {
59602             
59603             cls = '';
59604             if (i == 0) {
59605                 cls += ' fc-event-start';
59606             }
59607             if ((i+1) == rows.length) {
59608                 cls += ' fc-event-end';
59609             }
59610             
59611             //Roo.log(ev.data);
59612             // how many rows should it span..
59613             var cg = this.eventTmpl.append(ctr,Roo.apply({
59614                 fccls : cls
59615                 
59616             }, ev.data) , true);
59617             
59618             
59619             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59620             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59621             cg.on('click', this.onEventClick, this, ev);
59622             
59623             ev.els.push(cg);
59624             
59625             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59626             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59627             //Roo.log(cg);
59628              
59629             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59630             cg.setWidth(ebox.right - sbox.x -2);
59631         }
59632     },
59633     
59634     renderEvents: function()
59635     {   
59636         // first make sure there is enough space..
59637         
59638         if (!this.eventTmpl) {
59639             this.eventTmpl = new Roo.Template(
59640                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59641                     '<div class="fc-event-inner">' +
59642                         '<span class="fc-event-time">{time}</span>' +
59643                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59644                     '</div>' +
59645                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59646                 '</div>'
59647             );
59648                 
59649         }
59650                
59651         
59652         
59653         this.cells.each(function(c) {
59654             //Roo.log(c.select('.fc-day-content div',true).first());
59655             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59656         });
59657         
59658         var ctr = this.view.el.select('.fc-event-container',true).first();
59659         
59660         var cls;
59661         this.eventStore.each(function(ev){
59662             
59663             this.renderEvent(ev);
59664              
59665              
59666         }, this);
59667         this.view.layout();
59668         
59669     },
59670     
59671     onEventEnter: function (e, el,event,d) {
59672         this.fireEvent('evententer', this, el, event);
59673     },
59674     
59675     onEventLeave: function (e, el,event,d) {
59676         this.fireEvent('eventleave', this, el, event);
59677     },
59678     
59679     onEventClick: function (e, el,event,d) {
59680         this.fireEvent('eventclick', this, el, event);
59681     },
59682     
59683     onMonthChange: function () {
59684         this.store.load();
59685     },
59686     
59687     onLoad: function () {
59688         
59689         //Roo.log('calendar onload');
59690 //         
59691         if(this.eventStore.getCount() > 0){
59692             
59693            
59694             
59695             this.eventStore.each(function(d){
59696                 
59697                 
59698                 // FIXME..
59699                 var add =   d.data;
59700                 if (typeof(add.end_dt) == 'undefined')  {
59701                     Roo.log("Missing End time in calendar data: ");
59702                     Roo.log(d);
59703                     return;
59704                 }
59705                 if (typeof(add.start_dt) == 'undefined')  {
59706                     Roo.log("Missing Start time in calendar data: ");
59707                     Roo.log(d);
59708                     return;
59709                 }
59710                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59711                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59712                 add.id = add.id || d.id;
59713                 add.title = add.title || '??';
59714                 
59715                 this.addItem(d);
59716                 
59717              
59718             },this);
59719         }
59720         
59721         this.renderEvents();
59722     }
59723     
59724
59725 });
59726 /*
59727  grid : {
59728                 xtype: 'Grid',
59729                 xns: Roo.grid,
59730                 listeners : {
59731                     render : function ()
59732                     {
59733                         _this.grid = this;
59734                         
59735                         if (!this.view.el.hasClass('course-timesheet')) {
59736                             this.view.el.addClass('course-timesheet');
59737                         }
59738                         if (this.tsStyle) {
59739                             this.ds.load({});
59740                             return; 
59741                         }
59742                         Roo.log('width');
59743                         Roo.log(_this.grid.view.el.getWidth());
59744                         
59745                         
59746                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59747                             '.course-timesheet .x-grid-row' : {
59748                                 height: '80px'
59749                             },
59750                             '.x-grid-row td' : {
59751                                 'vertical-align' : 0
59752                             },
59753                             '.course-edit-link' : {
59754                                 'color' : 'blue',
59755                                 'text-overflow' : 'ellipsis',
59756                                 'overflow' : 'hidden',
59757                                 'white-space' : 'nowrap',
59758                                 'cursor' : 'pointer'
59759                             },
59760                             '.sub-link' : {
59761                                 'color' : 'green'
59762                             },
59763                             '.de-act-sup-link' : {
59764                                 'color' : 'purple',
59765                                 'text-decoration' : 'line-through'
59766                             },
59767                             '.de-act-link' : {
59768                                 'color' : 'red',
59769                                 'text-decoration' : 'line-through'
59770                             },
59771                             '.course-timesheet .course-highlight' : {
59772                                 'border-top-style': 'dashed !important',
59773                                 'border-bottom-bottom': 'dashed !important'
59774                             },
59775                             '.course-timesheet .course-item' : {
59776                                 'font-family'   : 'tahoma, arial, helvetica',
59777                                 'font-size'     : '11px',
59778                                 'overflow'      : 'hidden',
59779                                 'padding-left'  : '10px',
59780                                 'padding-right' : '10px',
59781                                 'padding-top' : '10px' 
59782                             }
59783                             
59784                         }, Roo.id());
59785                                 this.ds.load({});
59786                     }
59787                 },
59788                 autoWidth : true,
59789                 monitorWindowResize : false,
59790                 cellrenderer : function(v,x,r)
59791                 {
59792                     return v;
59793                 },
59794                 sm : {
59795                     xtype: 'CellSelectionModel',
59796                     xns: Roo.grid
59797                 },
59798                 dataSource : {
59799                     xtype: 'Store',
59800                     xns: Roo.data,
59801                     listeners : {
59802                         beforeload : function (_self, options)
59803                         {
59804                             options.params = options.params || {};
59805                             options.params._month = _this.monthField.getValue();
59806                             options.params.limit = 9999;
59807                             options.params['sort'] = 'when_dt';    
59808                             options.params['dir'] = 'ASC';    
59809                             this.proxy.loadResponse = this.loadResponse;
59810                             Roo.log("load?");
59811                             //this.addColumns();
59812                         },
59813                         load : function (_self, records, options)
59814                         {
59815                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59816                                 // if you click on the translation.. you can edit it...
59817                                 var el = Roo.get(this);
59818                                 var id = el.dom.getAttribute('data-id');
59819                                 var d = el.dom.getAttribute('data-date');
59820                                 var t = el.dom.getAttribute('data-time');
59821                                 //var id = this.child('span').dom.textContent;
59822                                 
59823                                 //Roo.log(this);
59824                                 Pman.Dialog.CourseCalendar.show({
59825                                     id : id,
59826                                     when_d : d,
59827                                     when_t : t,
59828                                     productitem_active : id ? 1 : 0
59829                                 }, function() {
59830                                     _this.grid.ds.load({});
59831                                 });
59832                            
59833                            });
59834                            
59835                            _this.panel.fireEvent('resize', [ '', '' ]);
59836                         }
59837                     },
59838                     loadResponse : function(o, success, response){
59839                             // this is overridden on before load..
59840                             
59841                             Roo.log("our code?");       
59842                             //Roo.log(success);
59843                             //Roo.log(response)
59844                             delete this.activeRequest;
59845                             if(!success){
59846                                 this.fireEvent("loadexception", this, o, response);
59847                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59848                                 return;
59849                             }
59850                             var result;
59851                             try {
59852                                 result = o.reader.read(response);
59853                             }catch(e){
59854                                 Roo.log("load exception?");
59855                                 this.fireEvent("loadexception", this, o, response, e);
59856                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59857                                 return;
59858                             }
59859                             Roo.log("ready...");        
59860                             // loop through result.records;
59861                             // and set this.tdate[date] = [] << array of records..
59862                             _this.tdata  = {};
59863                             Roo.each(result.records, function(r){
59864                                 //Roo.log(r.data);
59865                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59866                                     _this.tdata[r.data.when_dt.format('j')] = [];
59867                                 }
59868                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59869                             });
59870                             
59871                             //Roo.log(_this.tdata);
59872                             
59873                             result.records = [];
59874                             result.totalRecords = 6;
59875                     
59876                             // let's generate some duumy records for the rows.
59877                             //var st = _this.dateField.getValue();
59878                             
59879                             // work out monday..
59880                             //st = st.add(Date.DAY, -1 * st.format('w'));
59881                             
59882                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59883                             
59884                             var firstOfMonth = date.getFirstDayOfMonth();
59885                             var days = date.getDaysInMonth();
59886                             var d = 1;
59887                             var firstAdded = false;
59888                             for (var i = 0; i < result.totalRecords ; i++) {
59889                                 //var d= st.add(Date.DAY, i);
59890                                 var row = {};
59891                                 var added = 0;
59892                                 for(var w = 0 ; w < 7 ; w++){
59893                                     if(!firstAdded && firstOfMonth != w){
59894                                         continue;
59895                                     }
59896                                     if(d > days){
59897                                         continue;
59898                                     }
59899                                     firstAdded = true;
59900                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59901                                     row['weekday'+w] = String.format(
59902                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59903                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59904                                                     d,
59905                                                     date.format('Y-m-')+dd
59906                                                 );
59907                                     added++;
59908                                     if(typeof(_this.tdata[d]) != 'undefined'){
59909                                         Roo.each(_this.tdata[d], function(r){
59910                                             var is_sub = '';
59911                                             var deactive = '';
59912                                             var id = r.id;
59913                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59914                                             if(r.parent_id*1>0){
59915                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59916                                                 id = r.parent_id;
59917                                             }
59918                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59919                                                 deactive = 'de-act-link';
59920                                             }
59921                                             
59922                                             row['weekday'+w] += String.format(
59923                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59924                                                     id, //0
59925                                                     r.product_id_name, //1
59926                                                     r.when_dt.format('h:ia'), //2
59927                                                     is_sub, //3
59928                                                     deactive, //4
59929                                                     desc // 5
59930                                             );
59931                                         });
59932                                     }
59933                                     d++;
59934                                 }
59935                                 
59936                                 // only do this if something added..
59937                                 if(added > 0){ 
59938                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
59939                                 }
59940                                 
59941                                 
59942                                 // push it twice. (second one with an hour..
59943                                 
59944                             }
59945                             //Roo.log(result);
59946                             this.fireEvent("load", this, o, o.request.arg);
59947                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
59948                         },
59949                     sortInfo : {field: 'when_dt', direction : 'ASC' },
59950                     proxy : {
59951                         xtype: 'HttpProxy',
59952                         xns: Roo.data,
59953                         method : 'GET',
59954                         url : baseURL + '/Roo/Shop_course.php'
59955                     },
59956                     reader : {
59957                         xtype: 'JsonReader',
59958                         xns: Roo.data,
59959                         id : 'id',
59960                         fields : [
59961                             {
59962                                 'name': 'id',
59963                                 'type': 'int'
59964                             },
59965                             {
59966                                 'name': 'when_dt',
59967                                 'type': 'string'
59968                             },
59969                             {
59970                                 'name': 'end_dt',
59971                                 'type': 'string'
59972                             },
59973                             {
59974                                 'name': 'parent_id',
59975                                 'type': 'int'
59976                             },
59977                             {
59978                                 'name': 'product_id',
59979                                 'type': 'int'
59980                             },
59981                             {
59982                                 'name': 'productitem_id',
59983                                 'type': 'int'
59984                             },
59985                             {
59986                                 'name': 'guid',
59987                                 'type': 'int'
59988                             }
59989                         ]
59990                     }
59991                 },
59992                 toolbar : {
59993                     xtype: 'Toolbar',
59994                     xns: Roo,
59995                     items : [
59996                         {
59997                             xtype: 'Button',
59998                             xns: Roo.Toolbar,
59999                             listeners : {
60000                                 click : function (_self, e)
60001                                 {
60002                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60003                                     sd.setMonth(sd.getMonth()-1);
60004                                     _this.monthField.setValue(sd.format('Y-m-d'));
60005                                     _this.grid.ds.load({});
60006                                 }
60007                             },
60008                             text : "Back"
60009                         },
60010                         {
60011                             xtype: 'Separator',
60012                             xns: Roo.Toolbar
60013                         },
60014                         {
60015                             xtype: 'MonthField',
60016                             xns: Roo.form,
60017                             listeners : {
60018                                 render : function (_self)
60019                                 {
60020                                     _this.monthField = _self;
60021                                    // _this.monthField.set  today
60022                                 },
60023                                 select : function (combo, date)
60024                                 {
60025                                     _this.grid.ds.load({});
60026                                 }
60027                             },
60028                             value : (function() { return new Date(); })()
60029                         },
60030                         {
60031                             xtype: 'Separator',
60032                             xns: Roo.Toolbar
60033                         },
60034                         {
60035                             xtype: 'TextItem',
60036                             xns: Roo.Toolbar,
60037                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60038                         },
60039                         {
60040                             xtype: 'Fill',
60041                             xns: Roo.Toolbar
60042                         },
60043                         {
60044                             xtype: 'Button',
60045                             xns: Roo.Toolbar,
60046                             listeners : {
60047                                 click : function (_self, e)
60048                                 {
60049                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60050                                     sd.setMonth(sd.getMonth()+1);
60051                                     _this.monthField.setValue(sd.format('Y-m-d'));
60052                                     _this.grid.ds.load({});
60053                                 }
60054                             },
60055                             text : "Next"
60056                         }
60057                     ]
60058                 },
60059                  
60060             }
60061         };
60062         
60063         *//*
60064  * Based on:
60065  * Ext JS Library 1.1.1
60066  * Copyright(c) 2006-2007, Ext JS, LLC.
60067  *
60068  * Originally Released Under LGPL - original licence link has changed is not relivant.
60069  *
60070  * Fork - LGPL
60071  * <script type="text/javascript">
60072  */
60073  
60074 /**
60075  * @class Roo.LoadMask
60076  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60077  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60078  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60079  * element's UpdateManager load indicator and will be destroyed after the initial load.
60080  * @constructor
60081  * Create a new LoadMask
60082  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60083  * @param {Object} config The config object
60084  */
60085 Roo.LoadMask = function(el, config){
60086     this.el = Roo.get(el);
60087     Roo.apply(this, config);
60088     if(this.store){
60089         this.store.on('beforeload', this.onBeforeLoad, this);
60090         this.store.on('load', this.onLoad, this);
60091         this.store.on('loadexception', this.onLoadException, this);
60092         this.removeMask = false;
60093     }else{
60094         var um = this.el.getUpdateManager();
60095         um.showLoadIndicator = false; // disable the default indicator
60096         um.on('beforeupdate', this.onBeforeLoad, this);
60097         um.on('update', this.onLoad, this);
60098         um.on('failure', this.onLoad, this);
60099         this.removeMask = true;
60100     }
60101 };
60102
60103 Roo.LoadMask.prototype = {
60104     /**
60105      * @cfg {Boolean} removeMask
60106      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60107      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60108      */
60109     /**
60110      * @cfg {String} msg
60111      * The text to display in a centered loading message box (defaults to 'Loading...')
60112      */
60113     msg : 'Loading...',
60114     /**
60115      * @cfg {String} msgCls
60116      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60117      */
60118     msgCls : 'x-mask-loading',
60119
60120     /**
60121      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60122      * @type Boolean
60123      */
60124     disabled: false,
60125
60126     /**
60127      * Disables the mask to prevent it from being displayed
60128      */
60129     disable : function(){
60130        this.disabled = true;
60131     },
60132
60133     /**
60134      * Enables the mask so that it can be displayed
60135      */
60136     enable : function(){
60137         this.disabled = false;
60138     },
60139     
60140     onLoadException : function()
60141     {
60142         Roo.log(arguments);
60143         
60144         if (typeof(arguments[3]) != 'undefined') {
60145             Roo.MessageBox.alert("Error loading",arguments[3]);
60146         } 
60147         /*
60148         try {
60149             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60150                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60151             }   
60152         } catch(e) {
60153             
60154         }
60155         */
60156     
60157         
60158         
60159         this.el.unmask(this.removeMask);
60160     },
60161     // private
60162     onLoad : function()
60163     {
60164         this.el.unmask(this.removeMask);
60165     },
60166
60167     // private
60168     onBeforeLoad : function(){
60169         if(!this.disabled){
60170             this.el.mask(this.msg, this.msgCls);
60171         }
60172     },
60173
60174     // private
60175     destroy : function(){
60176         if(this.store){
60177             this.store.un('beforeload', this.onBeforeLoad, this);
60178             this.store.un('load', this.onLoad, this);
60179             this.store.un('loadexception', this.onLoadException, this);
60180         }else{
60181             var um = this.el.getUpdateManager();
60182             um.un('beforeupdate', this.onBeforeLoad, this);
60183             um.un('update', this.onLoad, this);
60184             um.un('failure', this.onLoad, this);
60185         }
60186     }
60187 };/*
60188  * Based on:
60189  * Ext JS Library 1.1.1
60190  * Copyright(c) 2006-2007, Ext JS, LLC.
60191  *
60192  * Originally Released Under LGPL - original licence link has changed is not relivant.
60193  *
60194  * Fork - LGPL
60195  * <script type="text/javascript">
60196  */
60197
60198
60199 /**
60200  * @class Roo.XTemplate
60201  * @extends Roo.Template
60202  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60203 <pre><code>
60204 var t = new Roo.XTemplate(
60205         '&lt;select name="{name}"&gt;',
60206                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60207         '&lt;/select&gt;'
60208 );
60209  
60210 // then append, applying the master template values
60211  </code></pre>
60212  *
60213  * Supported features:
60214  *
60215  *  Tags:
60216
60217 <pre><code>
60218       {a_variable} - output encoded.
60219       {a_variable.format:("Y-m-d")} - call a method on the variable
60220       {a_variable:raw} - unencoded output
60221       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60222       {a_variable:this.method_on_template(...)} - call a method on the template object.
60223  
60224 </code></pre>
60225  *  The tpl tag:
60226 <pre><code>
60227         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60228         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60229         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60230         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60231   
60232         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60233         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60234 </code></pre>
60235  *      
60236  */
60237 Roo.XTemplate = function()
60238 {
60239     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60240     if (this.html) {
60241         this.compile();
60242     }
60243 };
60244
60245
60246 Roo.extend(Roo.XTemplate, Roo.Template, {
60247
60248     /**
60249      * The various sub templates
60250      */
60251     tpls : false,
60252     /**
60253      *
60254      * basic tag replacing syntax
60255      * WORD:WORD()
60256      *
60257      * // you can fake an object call by doing this
60258      *  x.t:(test,tesT) 
60259      * 
60260      */
60261     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60262
60263     /**
60264      * compile the template
60265      *
60266      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60267      *
60268      */
60269     compile: function()
60270     {
60271         var s = this.html;
60272      
60273         s = ['<tpl>', s, '</tpl>'].join('');
60274     
60275         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60276             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60277             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60278             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60279             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60280             m,
60281             id     = 0,
60282             tpls   = [];
60283     
60284         while(true == !!(m = s.match(re))){
60285             var forMatch   = m[0].match(nameRe),
60286                 ifMatch   = m[0].match(ifRe),
60287                 execMatch   = m[0].match(execRe),
60288                 namedMatch   = m[0].match(namedRe),
60289                 
60290                 exp  = null, 
60291                 fn   = null,
60292                 exec = null,
60293                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60294                 
60295             if (ifMatch) {
60296                 // if - puts fn into test..
60297                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60298                 if(exp){
60299                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60300                 }
60301             }
60302             
60303             if (execMatch) {
60304                 // exec - calls a function... returns empty if true is  returned.
60305                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60306                 if(exp){
60307                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60308                 }
60309             }
60310             
60311             
60312             if (name) {
60313                 // for = 
60314                 switch(name){
60315                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60316                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60317                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60318                 }
60319             }
60320             var uid = namedMatch ? namedMatch[1] : id;
60321             
60322             
60323             tpls.push({
60324                 id:     namedMatch ? namedMatch[1] : id,
60325                 target: name,
60326                 exec:   exec,
60327                 test:   fn,
60328                 body:   m[1] || ''
60329             });
60330             if (namedMatch) {
60331                 s = s.replace(m[0], '');
60332             } else { 
60333                 s = s.replace(m[0], '{xtpl'+ id + '}');
60334             }
60335             ++id;
60336         }
60337         this.tpls = [];
60338         for(var i = tpls.length-1; i >= 0; --i){
60339             this.compileTpl(tpls[i]);
60340             this.tpls[tpls[i].id] = tpls[i];
60341         }
60342         this.master = tpls[tpls.length-1];
60343         return this;
60344     },
60345     /**
60346      * same as applyTemplate, except it's done to one of the subTemplates
60347      * when using named templates, you can do:
60348      *
60349      * var str = pl.applySubTemplate('your-name', values);
60350      *
60351      * 
60352      * @param {Number} id of the template
60353      * @param {Object} values to apply to template
60354      * @param {Object} parent (normaly the instance of this object)
60355      */
60356     applySubTemplate : function(id, values, parent)
60357     {
60358         
60359         
60360         var t = this.tpls[id];
60361         
60362         
60363         try { 
60364             if(t.test && !t.test.call(this, values, parent)){
60365                 return '';
60366             }
60367         } catch(e) {
60368             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60369             Roo.log(e.toString());
60370             Roo.log(t.test);
60371             return ''
60372         }
60373         try { 
60374             
60375             if(t.exec && t.exec.call(this, values, parent)){
60376                 return '';
60377             }
60378         } catch(e) {
60379             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60380             Roo.log(e.toString());
60381             Roo.log(t.exec);
60382             return ''
60383         }
60384         try {
60385             var vs = t.target ? t.target.call(this, values, parent) : values;
60386             parent = t.target ? values : parent;
60387             if(t.target && vs instanceof Array){
60388                 var buf = [];
60389                 for(var i = 0, len = vs.length; i < len; i++){
60390                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60391                 }
60392                 return buf.join('');
60393             }
60394             return t.compiled.call(this, vs, parent);
60395         } catch (e) {
60396             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60397             Roo.log(e.toString());
60398             Roo.log(t.compiled);
60399             return '';
60400         }
60401     },
60402
60403     compileTpl : function(tpl)
60404     {
60405         var fm = Roo.util.Format;
60406         var useF = this.disableFormats !== true;
60407         var sep = Roo.isGecko ? "+" : ",";
60408         var undef = function(str) {
60409             Roo.log("Property not found :"  + str);
60410             return '';
60411         };
60412         
60413         var fn = function(m, name, format, args)
60414         {
60415             //Roo.log(arguments);
60416             args = args ? args.replace(/\\'/g,"'") : args;
60417             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60418             if (typeof(format) == 'undefined') {
60419                 format= 'htmlEncode';
60420             }
60421             if (format == 'raw' ) {
60422                 format = false;
60423             }
60424             
60425             if(name.substr(0, 4) == 'xtpl'){
60426                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60427             }
60428             
60429             // build an array of options to determine if value is undefined..
60430             
60431             // basically get 'xxxx.yyyy' then do
60432             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60433             //    (function () { Roo.log("Property not found"); return ''; })() :
60434             //    ......
60435             
60436             var udef_ar = [];
60437             var lookfor = '';
60438             Roo.each(name.split('.'), function(st) {
60439                 lookfor += (lookfor.length ? '.': '') + st;
60440                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60441             });
60442             
60443             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60444             
60445             
60446             if(format && useF){
60447                 
60448                 args = args ? ',' + args : "";
60449                  
60450                 if(format.substr(0, 5) != "this."){
60451                     format = "fm." + format + '(';
60452                 }else{
60453                     format = 'this.call("'+ format.substr(5) + '", ';
60454                     args = ", values";
60455                 }
60456                 
60457                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60458             }
60459              
60460             if (args.length) {
60461                 // called with xxyx.yuu:(test,test)
60462                 // change to ()
60463                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60464             }
60465             // raw.. - :raw modifier..
60466             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60467             
60468         };
60469         var body;
60470         // branched to use + in gecko and [].join() in others
60471         if(Roo.isGecko){
60472             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60473                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60474                     "';};};";
60475         }else{
60476             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60477             body.push(tpl.body.replace(/(\r\n|\n)/g,
60478                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60479             body.push("'].join('');};};");
60480             body = body.join('');
60481         }
60482         
60483         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60484        
60485         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60486         eval(body);
60487         
60488         return this;
60489     },
60490
60491     applyTemplate : function(values){
60492         return this.master.compiled.call(this, values, {});
60493         //var s = this.subs;
60494     },
60495
60496     apply : function(){
60497         return this.applyTemplate.apply(this, arguments);
60498     }
60499
60500  });
60501
60502 Roo.XTemplate.from = function(el){
60503     el = Roo.getDom(el);
60504     return new Roo.XTemplate(el.value || el.innerHTML);
60505 };