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         DELETE : 46,
6772         /** Key constant 
6773         * @type Number */
6774         F5 : 116,
6775
6776            /** @private */
6777         setEvent : function(e){
6778             if(e == this || (e && e.browserEvent)){ // already wrapped
6779                 return e;
6780             }
6781             this.browserEvent = e;
6782             if(e){
6783                 // normalize buttons
6784                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6785                 if(e.type == 'click' && this.button == -1){
6786                     this.button = 0;
6787                 }
6788                 this.type = e.type;
6789                 this.shiftKey = e.shiftKey;
6790                 // mac metaKey behaves like ctrlKey
6791                 this.ctrlKey = e.ctrlKey || e.metaKey;
6792                 this.altKey = e.altKey;
6793                 // in getKey these will be normalized for the mac
6794                 this.keyCode = e.keyCode;
6795                 // keyup warnings on firefox.
6796                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6797                 // cache the target for the delayed and or buffered events
6798                 this.target = E.getTarget(e);
6799                 // same for XY
6800                 this.xy = E.getXY(e);
6801             }else{
6802                 this.button = -1;
6803                 this.shiftKey = false;
6804                 this.ctrlKey = false;
6805                 this.altKey = false;
6806                 this.keyCode = 0;
6807                 this.charCode =0;
6808                 this.target = null;
6809                 this.xy = [0, 0];
6810             }
6811             return this;
6812         },
6813
6814         /**
6815          * Stop the event (preventDefault and stopPropagation)
6816          */
6817         stopEvent : function(){
6818             if(this.browserEvent){
6819                 if(this.browserEvent.type == 'mousedown'){
6820                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6821                 }
6822                 E.stopEvent(this.browserEvent);
6823             }
6824         },
6825
6826         /**
6827          * Prevents the browsers default handling of the event.
6828          */
6829         preventDefault : function(){
6830             if(this.browserEvent){
6831                 E.preventDefault(this.browserEvent);
6832             }
6833         },
6834
6835         /** @private */
6836         isNavKeyPress : function(){
6837             var k = this.keyCode;
6838             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6839             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6840         },
6841
6842         isSpecialKey : function(){
6843             var k = this.keyCode;
6844             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6845             (k == 16) || (k == 17) ||
6846             (k >= 18 && k <= 20) ||
6847             (k >= 33 && k <= 35) ||
6848             (k >= 36 && k <= 39) ||
6849             (k >= 44 && k <= 45);
6850         },
6851         /**
6852          * Cancels bubbling of the event.
6853          */
6854         stopPropagation : function(){
6855             if(this.browserEvent){
6856                 if(this.type == 'mousedown'){
6857                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6858                 }
6859                 E.stopPropagation(this.browserEvent);
6860             }
6861         },
6862
6863         /**
6864          * Gets the key code for the event.
6865          * @return {Number}
6866          */
6867         getCharCode : function(){
6868             return this.charCode || this.keyCode;
6869         },
6870
6871         /**
6872          * Returns a normalized keyCode for the event.
6873          * @return {Number} The key code
6874          */
6875         getKey : function(){
6876             var k = this.keyCode || this.charCode;
6877             return Roo.isSafari ? (safariKeys[k] || k) : k;
6878         },
6879
6880         /**
6881          * Gets the x coordinate of the event.
6882          * @return {Number}
6883          */
6884         getPageX : function(){
6885             return this.xy[0];
6886         },
6887
6888         /**
6889          * Gets the y coordinate of the event.
6890          * @return {Number}
6891          */
6892         getPageY : function(){
6893             return this.xy[1];
6894         },
6895
6896         /**
6897          * Gets the time of the event.
6898          * @return {Number}
6899          */
6900         getTime : function(){
6901             if(this.browserEvent){
6902                 return E.getTime(this.browserEvent);
6903             }
6904             return null;
6905         },
6906
6907         /**
6908          * Gets the page coordinates of the event.
6909          * @return {Array} The xy values like [x, y]
6910          */
6911         getXY : function(){
6912             return this.xy;
6913         },
6914
6915         /**
6916          * Gets the target for the event.
6917          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6918          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6919                 search as a number or element (defaults to 10 || document.body)
6920          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6921          * @return {HTMLelement}
6922          */
6923         getTarget : function(selector, maxDepth, returnEl){
6924             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6925         },
6926         /**
6927          * Gets the related target.
6928          * @return {HTMLElement}
6929          */
6930         getRelatedTarget : function(){
6931             if(this.browserEvent){
6932                 return E.getRelatedTarget(this.browserEvent);
6933             }
6934             return null;
6935         },
6936
6937         /**
6938          * Normalizes mouse wheel delta across browsers
6939          * @return {Number} The delta
6940          */
6941         getWheelDelta : function(){
6942             var e = this.browserEvent;
6943             var delta = 0;
6944             if(e.wheelDelta){ /* IE/Opera. */
6945                 delta = e.wheelDelta/120;
6946             }else if(e.detail){ /* Mozilla case. */
6947                 delta = -e.detail/3;
6948             }
6949             return delta;
6950         },
6951
6952         /**
6953          * Returns true if the control, meta, shift or alt key was pressed during this event.
6954          * @return {Boolean}
6955          */
6956         hasModifier : function(){
6957             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6958         },
6959
6960         /**
6961          * Returns true if the target of this event equals el or is a child of el
6962          * @param {String/HTMLElement/Element} el
6963          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6964          * @return {Boolean}
6965          */
6966         within : function(el, related){
6967             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6968             return t && Roo.fly(el).contains(t);
6969         },
6970
6971         getPoint : function(){
6972             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6973         }
6974     };
6975
6976     return new Roo.EventObjectImpl();
6977 }();
6978             
6979     /*
6980  * Based on:
6981  * Ext JS Library 1.1.1
6982  * Copyright(c) 2006-2007, Ext JS, LLC.
6983  *
6984  * Originally Released Under LGPL - original licence link has changed is not relivant.
6985  *
6986  * Fork - LGPL
6987  * <script type="text/javascript">
6988  */
6989
6990  
6991 // was in Composite Element!??!?!
6992  
6993 (function(){
6994     var D = Roo.lib.Dom;
6995     var E = Roo.lib.Event;
6996     var A = Roo.lib.Anim;
6997
6998     // local style camelizing for speed
6999     var propCache = {};
7000     var camelRe = /(-[a-z])/gi;
7001     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7002     var view = document.defaultView;
7003
7004 /**
7005  * @class Roo.Element
7006  * Represents an Element in the DOM.<br><br>
7007  * Usage:<br>
7008 <pre><code>
7009 var el = Roo.get("my-div");
7010
7011 // or with getEl
7012 var el = getEl("my-div");
7013
7014 // or with a DOM element
7015 var el = Roo.get(myDivElement);
7016 </code></pre>
7017  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7018  * each call instead of constructing a new one.<br><br>
7019  * <b>Animations</b><br />
7020  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7021  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7022 <pre>
7023 Option    Default   Description
7024 --------- --------  ---------------------------------------------
7025 duration  .35       The duration of the animation in seconds
7026 easing    easeOut   The YUI easing method
7027 callback  none      A function to execute when the anim completes
7028 scope     this      The scope (this) of the callback function
7029 </pre>
7030 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7031 * manipulate the animation. Here's an example:
7032 <pre><code>
7033 var el = Roo.get("my-div");
7034
7035 // no animation
7036 el.setWidth(100);
7037
7038 // default animation
7039 el.setWidth(100, true);
7040
7041 // animation with some options set
7042 el.setWidth(100, {
7043     duration: 1,
7044     callback: this.foo,
7045     scope: this
7046 });
7047
7048 // using the "anim" property to get the Anim object
7049 var opt = {
7050     duration: 1,
7051     callback: this.foo,
7052     scope: this
7053 };
7054 el.setWidth(100, opt);
7055 ...
7056 if(opt.anim.isAnimated()){
7057     opt.anim.stop();
7058 }
7059 </code></pre>
7060 * <b> Composite (Collections of) Elements</b><br />
7061  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7062  * @constructor Create a new Element directly.
7063  * @param {String/HTMLElement} element
7064  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7065  */
7066     Roo.Element = function(element, forceNew){
7067         var dom = typeof element == "string" ?
7068                 document.getElementById(element) : element;
7069         if(!dom){ // invalid id/element
7070             return null;
7071         }
7072         var id = dom.id;
7073         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7074             return Roo.Element.cache[id];
7075         }
7076
7077         /**
7078          * The DOM element
7079          * @type HTMLElement
7080          */
7081         this.dom = dom;
7082
7083         /**
7084          * The DOM element ID
7085          * @type String
7086          */
7087         this.id = id || Roo.id(dom);
7088     };
7089
7090     var El = Roo.Element;
7091
7092     El.prototype = {
7093         /**
7094          * The element's default display mode  (defaults to "")
7095          * @type String
7096          */
7097         originalDisplay : "",
7098
7099         visibilityMode : 1,
7100         /**
7101          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7102          * @type String
7103          */
7104         defaultUnit : "px",
7105         
7106         /**
7107          * Sets the element's visibility mode. When setVisible() is called it
7108          * will use this to determine whether to set the visibility or the display property.
7109          * @param visMode Element.VISIBILITY or Element.DISPLAY
7110          * @return {Roo.Element} this
7111          */
7112         setVisibilityMode : function(visMode){
7113             this.visibilityMode = visMode;
7114             return this;
7115         },
7116         /**
7117          * Convenience method for setVisibilityMode(Element.DISPLAY)
7118          * @param {String} display (optional) What to set display to when visible
7119          * @return {Roo.Element} this
7120          */
7121         enableDisplayMode : function(display){
7122             this.setVisibilityMode(El.DISPLAY);
7123             if(typeof display != "undefined") { this.originalDisplay = display; }
7124             return this;
7125         },
7126
7127         /**
7128          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7129          * @param {String} selector The simple selector to test
7130          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7131                 search as a number or element (defaults to 10 || document.body)
7132          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7133          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7134          */
7135         findParent : function(simpleSelector, maxDepth, returnEl){
7136             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7137             maxDepth = maxDepth || 50;
7138             if(typeof maxDepth != "number"){
7139                 stopEl = Roo.getDom(maxDepth);
7140                 maxDepth = 10;
7141             }
7142             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7143                 if(dq.is(p, simpleSelector)){
7144                     return returnEl ? Roo.get(p) : p;
7145                 }
7146                 depth++;
7147                 p = p.parentNode;
7148             }
7149             return null;
7150         },
7151
7152
7153         /**
7154          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7155          * @param {String} selector The simple selector to test
7156          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7157                 search as a number or element (defaults to 10 || document.body)
7158          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7159          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7160          */
7161         findParentNode : function(simpleSelector, maxDepth, returnEl){
7162             var p = Roo.fly(this.dom.parentNode, '_internal');
7163             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7164         },
7165         
7166         /**
7167          * Looks at  the scrollable parent element
7168          */
7169         findScrollableParent : function(){
7170             
7171             var el = Roo.get(this.dom.parentNode);
7172             
7173             while (
7174                     el && 
7175                     !el.isScrollable() && 
7176                     D.getViewHeight() - el.dom.clientHeight <= 15 &&
7177                     D.getViewWidth() - el.dom.clientWidth <= 15 &&
7178                     el.dom.nodeName.toLowerCase() != 'body'
7179             ){
7180                 el = Roo.get(el.dom.parentNode);
7181             }
7182             
7183             if(!el.isScrollable()){
7184                 return null;
7185             }
7186             
7187             return el;
7188         },
7189
7190         /**
7191          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7192          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7193          * @param {String} selector The simple selector to test
7194          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7195                 search as a number or element (defaults to 10 || document.body)
7196          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7197          */
7198         up : function(simpleSelector, maxDepth){
7199             return this.findParentNode(simpleSelector, maxDepth, true);
7200         },
7201
7202
7203
7204         /**
7205          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7206          * @param {String} selector The simple selector to test
7207          * @return {Boolean} True if this element matches the selector, else false
7208          */
7209         is : function(simpleSelector){
7210             return Roo.DomQuery.is(this.dom, simpleSelector);
7211         },
7212
7213         /**
7214          * Perform animation on this element.
7215          * @param {Object} args The YUI animation control args
7216          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7217          * @param {Function} onComplete (optional) Function to call when animation completes
7218          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7219          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7220          * @return {Roo.Element} this
7221          */
7222         animate : function(args, duration, onComplete, easing, animType){
7223             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7224             return this;
7225         },
7226
7227         /*
7228          * @private Internal animation call
7229          */
7230         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7231             animType = animType || 'run';
7232             opt = opt || {};
7233             var anim = Roo.lib.Anim[animType](
7234                 this.dom, args,
7235                 (opt.duration || defaultDur) || .35,
7236                 (opt.easing || defaultEase) || 'easeOut',
7237                 function(){
7238                     Roo.callback(cb, this);
7239                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7240                 },
7241                 this
7242             );
7243             opt.anim = anim;
7244             return anim;
7245         },
7246
7247         // private legacy anim prep
7248         preanim : function(a, i){
7249             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7250         },
7251
7252         /**
7253          * Removes worthless text nodes
7254          * @param {Boolean} forceReclean (optional) By default the element
7255          * keeps track if it has been cleaned already so
7256          * you can call this over and over. However, if you update the element and
7257          * need to force a reclean, you can pass true.
7258          */
7259         clean : function(forceReclean){
7260             if(this.isCleaned && forceReclean !== true){
7261                 return this;
7262             }
7263             var ns = /\S/;
7264             var d = this.dom, n = d.firstChild, ni = -1;
7265             while(n){
7266                 var nx = n.nextSibling;
7267                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7268                     d.removeChild(n);
7269                 }else{
7270                     n.nodeIndex = ++ni;
7271                 }
7272                 n = nx;
7273             }
7274             this.isCleaned = true;
7275             return this;
7276         },
7277
7278         // private
7279         calcOffsetsTo : function(el){
7280             el = Roo.get(el);
7281             var d = el.dom;
7282             var restorePos = false;
7283             if(el.getStyle('position') == 'static'){
7284                 el.position('relative');
7285                 restorePos = true;
7286             }
7287             var x = 0, y =0;
7288             var op = this.dom;
7289             while(op && op != d && op.tagName != 'HTML'){
7290                 x+= op.offsetLeft;
7291                 y+= op.offsetTop;
7292                 op = op.offsetParent;
7293             }
7294             if(restorePos){
7295                 el.position('static');
7296             }
7297             return [x, y];
7298         },
7299
7300         /**
7301          * Scrolls this element into view within the passed container.
7302          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7303          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7304          * @return {Roo.Element} this
7305          */
7306         scrollIntoView : function(container, hscroll){
7307             var c = Roo.getDom(container) || document.body;
7308             var el = this.dom;
7309
7310             var o = this.calcOffsetsTo(c),
7311                 l = o[0],
7312                 t = o[1],
7313                 b = t+el.offsetHeight,
7314                 r = l+el.offsetWidth;
7315
7316             var ch = c.clientHeight;
7317             var ct = parseInt(c.scrollTop, 10);
7318             var cl = parseInt(c.scrollLeft, 10);
7319             var cb = ct + ch;
7320             var cr = cl + c.clientWidth;
7321
7322             if(t < ct){
7323                 c.scrollTop = t;
7324             }else if(b > cb){
7325                 c.scrollTop = b-ch;
7326             }
7327
7328             if(hscroll !== false){
7329                 if(l < cl){
7330                     c.scrollLeft = l;
7331                 }else if(r > cr){
7332                     c.scrollLeft = r-c.clientWidth;
7333                 }
7334             }
7335             return this;
7336         },
7337
7338         // private
7339         scrollChildIntoView : function(child, hscroll){
7340             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7341         },
7342
7343         /**
7344          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7345          * the new height may not be available immediately.
7346          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7347          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7348          * @param {Function} onComplete (optional) Function to call when animation completes
7349          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7350          * @return {Roo.Element} this
7351          */
7352         autoHeight : function(animate, duration, onComplete, easing){
7353             var oldHeight = this.getHeight();
7354             this.clip();
7355             this.setHeight(1); // force clipping
7356             setTimeout(function(){
7357                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7358                 if(!animate){
7359                     this.setHeight(height);
7360                     this.unclip();
7361                     if(typeof onComplete == "function"){
7362                         onComplete();
7363                     }
7364                 }else{
7365                     this.setHeight(oldHeight); // restore original height
7366                     this.setHeight(height, animate, duration, function(){
7367                         this.unclip();
7368                         if(typeof onComplete == "function") { onComplete(); }
7369                     }.createDelegate(this), easing);
7370                 }
7371             }.createDelegate(this), 0);
7372             return this;
7373         },
7374
7375         /**
7376          * Returns true if this element is an ancestor of the passed element
7377          * @param {HTMLElement/String} el The element to check
7378          * @return {Boolean} True if this element is an ancestor of el, else false
7379          */
7380         contains : function(el){
7381             if(!el){return false;}
7382             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7383         },
7384
7385         /**
7386          * Checks whether the element is currently visible using both visibility and display properties.
7387          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7388          * @return {Boolean} True if the element is currently visible, else false
7389          */
7390         isVisible : function(deep) {
7391             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7392             if(deep !== true || !vis){
7393                 return vis;
7394             }
7395             var p = this.dom.parentNode;
7396             while(p && p.tagName.toLowerCase() != "body"){
7397                 if(!Roo.fly(p, '_isVisible').isVisible()){
7398                     return false;
7399                 }
7400                 p = p.parentNode;
7401             }
7402             return true;
7403         },
7404
7405         /**
7406          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7407          * @param {String} selector The CSS selector
7408          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7409          * @return {CompositeElement/CompositeElementLite} The composite element
7410          */
7411         select : function(selector, unique){
7412             return El.select(selector, unique, this.dom);
7413         },
7414
7415         /**
7416          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7417          * @param {String} selector The CSS selector
7418          * @return {Array} An array of the matched nodes
7419          */
7420         query : function(selector, unique){
7421             return Roo.DomQuery.select(selector, this.dom);
7422         },
7423
7424         /**
7425          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7426          * @param {String} selector The CSS selector
7427          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7428          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7429          */
7430         child : function(selector, returnDom){
7431             var n = Roo.DomQuery.selectNode(selector, this.dom);
7432             return returnDom ? n : Roo.get(n);
7433         },
7434
7435         /**
7436          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7437          * @param {String} selector The CSS selector
7438          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7439          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7440          */
7441         down : function(selector, returnDom){
7442             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7443             return returnDom ? n : Roo.get(n);
7444         },
7445
7446         /**
7447          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7448          * @param {String} group The group the DD object is member of
7449          * @param {Object} config The DD config object
7450          * @param {Object} overrides An object containing methods to override/implement on the DD object
7451          * @return {Roo.dd.DD} The DD object
7452          */
7453         initDD : function(group, config, overrides){
7454             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7455             return Roo.apply(dd, overrides);
7456         },
7457
7458         /**
7459          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7460          * @param {String} group The group the DDProxy object is member of
7461          * @param {Object} config The DDProxy config object
7462          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7463          * @return {Roo.dd.DDProxy} The DDProxy object
7464          */
7465         initDDProxy : function(group, config, overrides){
7466             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7467             return Roo.apply(dd, overrides);
7468         },
7469
7470         /**
7471          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7472          * @param {String} group The group the DDTarget object is member of
7473          * @param {Object} config The DDTarget config object
7474          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7475          * @return {Roo.dd.DDTarget} The DDTarget object
7476          */
7477         initDDTarget : function(group, config, overrides){
7478             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7479             return Roo.apply(dd, overrides);
7480         },
7481
7482         /**
7483          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7484          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7485          * @param {Boolean} visible Whether the element is visible
7486          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7487          * @return {Roo.Element} this
7488          */
7489          setVisible : function(visible, animate){
7490             if(!animate || !A){
7491                 if(this.visibilityMode == El.DISPLAY){
7492                     this.setDisplayed(visible);
7493                 }else{
7494                     this.fixDisplay();
7495                     this.dom.style.visibility = visible ? "visible" : "hidden";
7496                 }
7497             }else{
7498                 // closure for composites
7499                 var dom = this.dom;
7500                 var visMode = this.visibilityMode;
7501                 if(visible){
7502                     this.setOpacity(.01);
7503                     this.setVisible(true);
7504                 }
7505                 this.anim({opacity: { to: (visible?1:0) }},
7506                       this.preanim(arguments, 1),
7507                       null, .35, 'easeIn', function(){
7508                          if(!visible){
7509                              if(visMode == El.DISPLAY){
7510                                  dom.style.display = "none";
7511                              }else{
7512                                  dom.style.visibility = "hidden";
7513                              }
7514                              Roo.get(dom).setOpacity(1);
7515                          }
7516                      });
7517             }
7518             return this;
7519         },
7520
7521         /**
7522          * Returns true if display is not "none"
7523          * @return {Boolean}
7524          */
7525         isDisplayed : function() {
7526             return this.getStyle("display") != "none";
7527         },
7528
7529         /**
7530          * Toggles the element's visibility or display, depending on visibility mode.
7531          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7532          * @return {Roo.Element} this
7533          */
7534         toggle : function(animate){
7535             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7536             return this;
7537         },
7538
7539         /**
7540          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7541          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7542          * @return {Roo.Element} this
7543          */
7544         setDisplayed : function(value) {
7545             if(typeof value == "boolean"){
7546                value = value ? this.originalDisplay : "none";
7547             }
7548             this.setStyle("display", value);
7549             return this;
7550         },
7551
7552         /**
7553          * Tries to focus the element. Any exceptions are caught and ignored.
7554          * @return {Roo.Element} this
7555          */
7556         focus : function() {
7557             try{
7558                 this.dom.focus();
7559             }catch(e){}
7560             return this;
7561         },
7562
7563         /**
7564          * Tries to blur the element. Any exceptions are caught and ignored.
7565          * @return {Roo.Element} this
7566          */
7567         blur : function() {
7568             try{
7569                 this.dom.blur();
7570             }catch(e){}
7571             return this;
7572         },
7573
7574         /**
7575          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7576          * @param {String/Array} className The CSS class to add, or an array of classes
7577          * @return {Roo.Element} this
7578          */
7579         addClass : function(className){
7580             if(className instanceof Array){
7581                 for(var i = 0, len = className.length; i < len; i++) {
7582                     this.addClass(className[i]);
7583                 }
7584             }else{
7585                 if(className && !this.hasClass(className)){
7586                     this.dom.className = this.dom.className + " " + className;
7587                 }
7588             }
7589             return this;
7590         },
7591
7592         /**
7593          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7594          * @param {String/Array} className The CSS class to add, or an array of classes
7595          * @return {Roo.Element} this
7596          */
7597         radioClass : function(className){
7598             var siblings = this.dom.parentNode.childNodes;
7599             for(var i = 0; i < siblings.length; i++) {
7600                 var s = siblings[i];
7601                 if(s.nodeType == 1){
7602                     Roo.get(s).removeClass(className);
7603                 }
7604             }
7605             this.addClass(className);
7606             return this;
7607         },
7608
7609         /**
7610          * Removes one or more CSS classes from the element.
7611          * @param {String/Array} className The CSS class to remove, or an array of classes
7612          * @return {Roo.Element} this
7613          */
7614         removeClass : function(className){
7615             if(!className || !this.dom.className){
7616                 return this;
7617             }
7618             if(className instanceof Array){
7619                 for(var i = 0, len = className.length; i < len; i++) {
7620                     this.removeClass(className[i]);
7621                 }
7622             }else{
7623                 if(this.hasClass(className)){
7624                     var re = this.classReCache[className];
7625                     if (!re) {
7626                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7627                        this.classReCache[className] = re;
7628                     }
7629                     this.dom.className =
7630                         this.dom.className.replace(re, " ");
7631                 }
7632             }
7633             return this;
7634         },
7635
7636         // private
7637         classReCache: {},
7638
7639         /**
7640          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7641          * @param {String} className The CSS class to toggle
7642          * @return {Roo.Element} this
7643          */
7644         toggleClass : function(className){
7645             if(this.hasClass(className)){
7646                 this.removeClass(className);
7647             }else{
7648                 this.addClass(className);
7649             }
7650             return this;
7651         },
7652
7653         /**
7654          * Checks if the specified CSS class exists on this element's DOM node.
7655          * @param {String} className The CSS class to check for
7656          * @return {Boolean} True if the class exists, else false
7657          */
7658         hasClass : function(className){
7659             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7660         },
7661
7662         /**
7663          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7664          * @param {String} oldClassName The CSS class to replace
7665          * @param {String} newClassName The replacement CSS class
7666          * @return {Roo.Element} this
7667          */
7668         replaceClass : function(oldClassName, newClassName){
7669             this.removeClass(oldClassName);
7670             this.addClass(newClassName);
7671             return this;
7672         },
7673
7674         /**
7675          * Returns an object with properties matching the styles requested.
7676          * For example, el.getStyles('color', 'font-size', 'width') might return
7677          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7678          * @param {String} style1 A style name
7679          * @param {String} style2 A style name
7680          * @param {String} etc.
7681          * @return {Object} The style object
7682          */
7683         getStyles : function(){
7684             var a = arguments, len = a.length, r = {};
7685             for(var i = 0; i < len; i++){
7686                 r[a[i]] = this.getStyle(a[i]);
7687             }
7688             return r;
7689         },
7690
7691         /**
7692          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7693          * @param {String} property The style property whose value is returned.
7694          * @return {String} The current value of the style property for this element.
7695          */
7696         getStyle : function(){
7697             return view && view.getComputedStyle ?
7698                 function(prop){
7699                     var el = this.dom, v, cs, camel;
7700                     if(prop == 'float'){
7701                         prop = "cssFloat";
7702                     }
7703                     if(el.style && (v = el.style[prop])){
7704                         return v;
7705                     }
7706                     if(cs = view.getComputedStyle(el, "")){
7707                         if(!(camel = propCache[prop])){
7708                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7709                         }
7710                         return cs[camel];
7711                     }
7712                     return null;
7713                 } :
7714                 function(prop){
7715                     var el = this.dom, v, cs, camel;
7716                     if(prop == 'opacity'){
7717                         if(typeof el.style.filter == 'string'){
7718                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7719                             if(m){
7720                                 var fv = parseFloat(m[1]);
7721                                 if(!isNaN(fv)){
7722                                     return fv ? fv / 100 : 0;
7723                                 }
7724                             }
7725                         }
7726                         return 1;
7727                     }else if(prop == 'float'){
7728                         prop = "styleFloat";
7729                     }
7730                     if(!(camel = propCache[prop])){
7731                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7732                     }
7733                     if(v = el.style[camel]){
7734                         return v;
7735                     }
7736                     if(cs = el.currentStyle){
7737                         return cs[camel];
7738                     }
7739                     return null;
7740                 };
7741         }(),
7742
7743         /**
7744          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7745          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7746          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7747          * @return {Roo.Element} this
7748          */
7749         setStyle : function(prop, value){
7750             if(typeof prop == "string"){
7751                 
7752                 if (prop == 'float') {
7753                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7754                     return this;
7755                 }
7756                 
7757                 var camel;
7758                 if(!(camel = propCache[prop])){
7759                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7760                 }
7761                 
7762                 if(camel == 'opacity') {
7763                     this.setOpacity(value);
7764                 }else{
7765                     this.dom.style[camel] = value;
7766                 }
7767             }else{
7768                 for(var style in prop){
7769                     if(typeof prop[style] != "function"){
7770                        this.setStyle(style, prop[style]);
7771                     }
7772                 }
7773             }
7774             return this;
7775         },
7776
7777         /**
7778          * More flexible version of {@link #setStyle} for setting style properties.
7779          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7780          * a function which returns such a specification.
7781          * @return {Roo.Element} this
7782          */
7783         applyStyles : function(style){
7784             Roo.DomHelper.applyStyles(this.dom, style);
7785             return this;
7786         },
7787
7788         /**
7789           * 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).
7790           * @return {Number} The X position of the element
7791           */
7792         getX : function(){
7793             return D.getX(this.dom);
7794         },
7795
7796         /**
7797           * 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).
7798           * @return {Number} The Y position of the element
7799           */
7800         getY : function(){
7801             return D.getY(this.dom);
7802         },
7803
7804         /**
7805           * 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).
7806           * @return {Array} The XY position of the element
7807           */
7808         getXY : function(){
7809             return D.getXY(this.dom);
7810         },
7811
7812         /**
7813          * 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).
7814          * @param {Number} The X position of the element
7815          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7816          * @return {Roo.Element} this
7817          */
7818         setX : function(x, animate){
7819             if(!animate || !A){
7820                 D.setX(this.dom, x);
7821             }else{
7822                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7823             }
7824             return this;
7825         },
7826
7827         /**
7828          * 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).
7829          * @param {Number} The Y position of the element
7830          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7831          * @return {Roo.Element} this
7832          */
7833         setY : function(y, animate){
7834             if(!animate || !A){
7835                 D.setY(this.dom, y);
7836             }else{
7837                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7838             }
7839             return this;
7840         },
7841
7842         /**
7843          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7844          * @param {String} left The left CSS property value
7845          * @return {Roo.Element} this
7846          */
7847         setLeft : function(left){
7848             this.setStyle("left", this.addUnits(left));
7849             return this;
7850         },
7851
7852         /**
7853          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7854          * @param {String} top The top CSS property value
7855          * @return {Roo.Element} this
7856          */
7857         setTop : function(top){
7858             this.setStyle("top", this.addUnits(top));
7859             return this;
7860         },
7861
7862         /**
7863          * Sets the element's CSS right style.
7864          * @param {String} right The right CSS property value
7865          * @return {Roo.Element} this
7866          */
7867         setRight : function(right){
7868             this.setStyle("right", this.addUnits(right));
7869             return this;
7870         },
7871
7872         /**
7873          * Sets the element's CSS bottom style.
7874          * @param {String} bottom The bottom CSS property value
7875          * @return {Roo.Element} this
7876          */
7877         setBottom : function(bottom){
7878             this.setStyle("bottom", this.addUnits(bottom));
7879             return this;
7880         },
7881
7882         /**
7883          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7884          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7885          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7886          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7887          * @return {Roo.Element} this
7888          */
7889         setXY : function(pos, animate){
7890             if(!animate || !A){
7891                 D.setXY(this.dom, pos);
7892             }else{
7893                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7894             }
7895             return this;
7896         },
7897
7898         /**
7899          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7900          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7901          * @param {Number} x X value for new position (coordinates are page-based)
7902          * @param {Number} y Y value for new position (coordinates are page-based)
7903          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7904          * @return {Roo.Element} this
7905          */
7906         setLocation : function(x, y, animate){
7907             this.setXY([x, y], this.preanim(arguments, 2));
7908             return this;
7909         },
7910
7911         /**
7912          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7913          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7914          * @param {Number} x X value for new position (coordinates are page-based)
7915          * @param {Number} y Y value for new position (coordinates are page-based)
7916          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7917          * @return {Roo.Element} this
7918          */
7919         moveTo : function(x, y, animate){
7920             this.setXY([x, y], this.preanim(arguments, 2));
7921             return this;
7922         },
7923
7924         /**
7925          * Returns the region of the given element.
7926          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7927          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7928          */
7929         getRegion : function(){
7930             return D.getRegion(this.dom);
7931         },
7932
7933         /**
7934          * Returns the offset height of the element
7935          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7936          * @return {Number} The element's height
7937          */
7938         getHeight : function(contentHeight){
7939             var h = this.dom.offsetHeight || 0;
7940             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7941         },
7942
7943         /**
7944          * Returns the offset width of the element
7945          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7946          * @return {Number} The element's width
7947          */
7948         getWidth : function(contentWidth){
7949             var w = this.dom.offsetWidth || 0;
7950             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7951         },
7952
7953         /**
7954          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7955          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7956          * if a height has not been set using CSS.
7957          * @return {Number}
7958          */
7959         getComputedHeight : function(){
7960             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7961             if(!h){
7962                 h = parseInt(this.getStyle('height'), 10) || 0;
7963                 if(!this.isBorderBox()){
7964                     h += this.getFrameWidth('tb');
7965                 }
7966             }
7967             return h;
7968         },
7969
7970         /**
7971          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7972          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7973          * if a width has not been set using CSS.
7974          * @return {Number}
7975          */
7976         getComputedWidth : function(){
7977             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7978             if(!w){
7979                 w = parseInt(this.getStyle('width'), 10) || 0;
7980                 if(!this.isBorderBox()){
7981                     w += this.getFrameWidth('lr');
7982                 }
7983             }
7984             return w;
7985         },
7986
7987         /**
7988          * Returns the size of the element.
7989          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7990          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7991          */
7992         getSize : function(contentSize){
7993             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7994         },
7995
7996         /**
7997          * Returns the width and height of the viewport.
7998          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7999          */
8000         getViewSize : function(){
8001             var d = this.dom, doc = document, aw = 0, ah = 0;
8002             if(d == doc || d == doc.body){
8003                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8004             }else{
8005                 return {
8006                     width : d.clientWidth,
8007                     height: d.clientHeight
8008                 };
8009             }
8010         },
8011
8012         /**
8013          * Returns the value of the "value" attribute
8014          * @param {Boolean} asNumber true to parse the value as a number
8015          * @return {String/Number}
8016          */
8017         getValue : function(asNumber){
8018             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8019         },
8020
8021         // private
8022         adjustWidth : function(width){
8023             if(typeof width == "number"){
8024                 if(this.autoBoxAdjust && !this.isBorderBox()){
8025                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8026                 }
8027                 if(width < 0){
8028                     width = 0;
8029                 }
8030             }
8031             return width;
8032         },
8033
8034         // private
8035         adjustHeight : function(height){
8036             if(typeof height == "number"){
8037                if(this.autoBoxAdjust && !this.isBorderBox()){
8038                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8039                }
8040                if(height < 0){
8041                    height = 0;
8042                }
8043             }
8044             return height;
8045         },
8046
8047         /**
8048          * Set the width of the element
8049          * @param {Number} width The new width
8050          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8051          * @return {Roo.Element} this
8052          */
8053         setWidth : function(width, animate){
8054             width = this.adjustWidth(width);
8055             if(!animate || !A){
8056                 this.dom.style.width = this.addUnits(width);
8057             }else{
8058                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8059             }
8060             return this;
8061         },
8062
8063         /**
8064          * Set the height of the element
8065          * @param {Number} height The new height
8066          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8067          * @return {Roo.Element} this
8068          */
8069          setHeight : function(height, animate){
8070             height = this.adjustHeight(height);
8071             if(!animate || !A){
8072                 this.dom.style.height = this.addUnits(height);
8073             }else{
8074                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8075             }
8076             return this;
8077         },
8078
8079         /**
8080          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8081          * @param {Number} width The new width
8082          * @param {Number} height The new height
8083          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8084          * @return {Roo.Element} this
8085          */
8086          setSize : function(width, height, animate){
8087             if(typeof width == "object"){ // in case of object from getSize()
8088                 height = width.height; width = width.width;
8089             }
8090             width = this.adjustWidth(width); height = this.adjustHeight(height);
8091             if(!animate || !A){
8092                 this.dom.style.width = this.addUnits(width);
8093                 this.dom.style.height = this.addUnits(height);
8094             }else{
8095                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8096             }
8097             return this;
8098         },
8099
8100         /**
8101          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8102          * @param {Number} x X value for new position (coordinates are page-based)
8103          * @param {Number} y Y value for new position (coordinates are page-based)
8104          * @param {Number} width The new width
8105          * @param {Number} height The new height
8106          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8107          * @return {Roo.Element} this
8108          */
8109         setBounds : function(x, y, width, height, animate){
8110             if(!animate || !A){
8111                 this.setSize(width, height);
8112                 this.setLocation(x, y);
8113             }else{
8114                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8115                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8116                               this.preanim(arguments, 4), 'motion');
8117             }
8118             return this;
8119         },
8120
8121         /**
8122          * 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.
8123          * @param {Roo.lib.Region} region The region to fill
8124          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8125          * @return {Roo.Element} this
8126          */
8127         setRegion : function(region, animate){
8128             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8129             return this;
8130         },
8131
8132         /**
8133          * Appends an event handler
8134          *
8135          * @param {String}   eventName     The type of event to append
8136          * @param {Function} fn        The method the event invokes
8137          * @param {Object} scope       (optional) The scope (this object) of the fn
8138          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8139          */
8140         addListener : function(eventName, fn, scope, options){
8141             if (this.dom) {
8142                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8143             }
8144         },
8145
8146         /**
8147          * Removes an event handler from this element
8148          * @param {String} eventName the type of event to remove
8149          * @param {Function} fn the method the event invokes
8150          * @return {Roo.Element} this
8151          */
8152         removeListener : function(eventName, fn){
8153             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8154             return this;
8155         },
8156
8157         /**
8158          * Removes all previous added listeners from this element
8159          * @return {Roo.Element} this
8160          */
8161         removeAllListeners : function(){
8162             E.purgeElement(this.dom);
8163             return this;
8164         },
8165
8166         relayEvent : function(eventName, observable){
8167             this.on(eventName, function(e){
8168                 observable.fireEvent(eventName, e);
8169             });
8170         },
8171
8172         /**
8173          * Set the opacity of the element
8174          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8175          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8176          * @return {Roo.Element} this
8177          */
8178          setOpacity : function(opacity, animate){
8179             if(!animate || !A){
8180                 var s = this.dom.style;
8181                 if(Roo.isIE){
8182                     s.zoom = 1;
8183                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8184                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8185                 }else{
8186                     s.opacity = opacity;
8187                 }
8188             }else{
8189                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8190             }
8191             return this;
8192         },
8193
8194         /**
8195          * Gets the left X coordinate
8196          * @param {Boolean} local True to get the local css position instead of page coordinate
8197          * @return {Number}
8198          */
8199         getLeft : function(local){
8200             if(!local){
8201                 return this.getX();
8202             }else{
8203                 return parseInt(this.getStyle("left"), 10) || 0;
8204             }
8205         },
8206
8207         /**
8208          * Gets the right X coordinate of the element (element X position + element width)
8209          * @param {Boolean} local True to get the local css position instead of page coordinate
8210          * @return {Number}
8211          */
8212         getRight : function(local){
8213             if(!local){
8214                 return this.getX() + this.getWidth();
8215             }else{
8216                 return (this.getLeft(true) + this.getWidth()) || 0;
8217             }
8218         },
8219
8220         /**
8221          * Gets the top Y coordinate
8222          * @param {Boolean} local True to get the local css position instead of page coordinate
8223          * @return {Number}
8224          */
8225         getTop : function(local) {
8226             if(!local){
8227                 return this.getY();
8228             }else{
8229                 return parseInt(this.getStyle("top"), 10) || 0;
8230             }
8231         },
8232
8233         /**
8234          * Gets the bottom Y coordinate of the element (element Y position + element height)
8235          * @param {Boolean} local True to get the local css position instead of page coordinate
8236          * @return {Number}
8237          */
8238         getBottom : function(local){
8239             if(!local){
8240                 return this.getY() + this.getHeight();
8241             }else{
8242                 return (this.getTop(true) + this.getHeight()) || 0;
8243             }
8244         },
8245
8246         /**
8247         * Initializes positioning on this element. If a desired position is not passed, it will make the
8248         * the element positioned relative IF it is not already positioned.
8249         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8250         * @param {Number} zIndex (optional) The zIndex to apply
8251         * @param {Number} x (optional) Set the page X position
8252         * @param {Number} y (optional) Set the page Y position
8253         */
8254         position : function(pos, zIndex, x, y){
8255             if(!pos){
8256                if(this.getStyle('position') == 'static'){
8257                    this.setStyle('position', 'relative');
8258                }
8259             }else{
8260                 this.setStyle("position", pos);
8261             }
8262             if(zIndex){
8263                 this.setStyle("z-index", zIndex);
8264             }
8265             if(x !== undefined && y !== undefined){
8266                 this.setXY([x, y]);
8267             }else if(x !== undefined){
8268                 this.setX(x);
8269             }else if(y !== undefined){
8270                 this.setY(y);
8271             }
8272         },
8273
8274         /**
8275         * Clear positioning back to the default when the document was loaded
8276         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8277         * @return {Roo.Element} this
8278          */
8279         clearPositioning : function(value){
8280             value = value ||'';
8281             this.setStyle({
8282                 "left": value,
8283                 "right": value,
8284                 "top": value,
8285                 "bottom": value,
8286                 "z-index": "",
8287                 "position" : "static"
8288             });
8289             return this;
8290         },
8291
8292         /**
8293         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8294         * snapshot before performing an update and then restoring the element.
8295         * @return {Object}
8296         */
8297         getPositioning : function(){
8298             var l = this.getStyle("left");
8299             var t = this.getStyle("top");
8300             return {
8301                 "position" : this.getStyle("position"),
8302                 "left" : l,
8303                 "right" : l ? "" : this.getStyle("right"),
8304                 "top" : t,
8305                 "bottom" : t ? "" : this.getStyle("bottom"),
8306                 "z-index" : this.getStyle("z-index")
8307             };
8308         },
8309
8310         /**
8311          * Gets the width of the border(s) for the specified side(s)
8312          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8313          * passing lr would get the border (l)eft width + the border (r)ight width.
8314          * @return {Number} The width of the sides passed added together
8315          */
8316         getBorderWidth : function(side){
8317             return this.addStyles(side, El.borders);
8318         },
8319
8320         /**
8321          * Gets the width of the padding(s) for the specified side(s)
8322          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8323          * passing lr would get the padding (l)eft + the padding (r)ight.
8324          * @return {Number} The padding of the sides passed added together
8325          */
8326         getPadding : function(side){
8327             return this.addStyles(side, El.paddings);
8328         },
8329
8330         /**
8331         * Set positioning with an object returned by getPositioning().
8332         * @param {Object} posCfg
8333         * @return {Roo.Element} this
8334          */
8335         setPositioning : function(pc){
8336             this.applyStyles(pc);
8337             if(pc.right == "auto"){
8338                 this.dom.style.right = "";
8339             }
8340             if(pc.bottom == "auto"){
8341                 this.dom.style.bottom = "";
8342             }
8343             return this;
8344         },
8345
8346         // private
8347         fixDisplay : function(){
8348             if(this.getStyle("display") == "none"){
8349                 this.setStyle("visibility", "hidden");
8350                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8351                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8352                     this.setStyle("display", "block");
8353                 }
8354             }
8355         },
8356
8357         /**
8358          * Quick set left and top adding default units
8359          * @param {String} left The left CSS property value
8360          * @param {String} top The top CSS property value
8361          * @return {Roo.Element} this
8362          */
8363          setLeftTop : function(left, top){
8364             this.dom.style.left = this.addUnits(left);
8365             this.dom.style.top = this.addUnits(top);
8366             return this;
8367         },
8368
8369         /**
8370          * Move this element relative to its current position.
8371          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8372          * @param {Number} distance How far to move the element in pixels
8373          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8374          * @return {Roo.Element} this
8375          */
8376          move : function(direction, distance, animate){
8377             var xy = this.getXY();
8378             direction = direction.toLowerCase();
8379             switch(direction){
8380                 case "l":
8381                 case "left":
8382                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8383                     break;
8384                case "r":
8385                case "right":
8386                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8387                     break;
8388                case "t":
8389                case "top":
8390                case "up":
8391                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8392                     break;
8393                case "b":
8394                case "bottom":
8395                case "down":
8396                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8397                     break;
8398             }
8399             return this;
8400         },
8401
8402         /**
8403          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8404          * @return {Roo.Element} this
8405          */
8406         clip : function(){
8407             if(!this.isClipped){
8408                this.isClipped = true;
8409                this.originalClip = {
8410                    "o": this.getStyle("overflow"),
8411                    "x": this.getStyle("overflow-x"),
8412                    "y": this.getStyle("overflow-y")
8413                };
8414                this.setStyle("overflow", "hidden");
8415                this.setStyle("overflow-x", "hidden");
8416                this.setStyle("overflow-y", "hidden");
8417             }
8418             return this;
8419         },
8420
8421         /**
8422          *  Return clipping (overflow) to original clipping before clip() was called
8423          * @return {Roo.Element} this
8424          */
8425         unclip : function(){
8426             if(this.isClipped){
8427                 this.isClipped = false;
8428                 var o = this.originalClip;
8429                 if(o.o){this.setStyle("overflow", o.o);}
8430                 if(o.x){this.setStyle("overflow-x", o.x);}
8431                 if(o.y){this.setStyle("overflow-y", o.y);}
8432             }
8433             return this;
8434         },
8435
8436
8437         /**
8438          * Gets the x,y coordinates specified by the anchor position on the element.
8439          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8440          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8441          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8442          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8443          * @return {Array} [x, y] An array containing the element's x and y coordinates
8444          */
8445         getAnchorXY : function(anchor, local, s){
8446             //Passing a different size is useful for pre-calculating anchors,
8447             //especially for anchored animations that change the el size.
8448
8449             var w, h, vp = false;
8450             if(!s){
8451                 var d = this.dom;
8452                 if(d == document.body || d == document){
8453                     vp = true;
8454                     w = D.getViewWidth(); h = D.getViewHeight();
8455                 }else{
8456                     w = this.getWidth(); h = this.getHeight();
8457                 }
8458             }else{
8459                 w = s.width;  h = s.height;
8460             }
8461             var x = 0, y = 0, r = Math.round;
8462             switch((anchor || "tl").toLowerCase()){
8463                 case "c":
8464                     x = r(w*.5);
8465                     y = r(h*.5);
8466                 break;
8467                 case "t":
8468                     x = r(w*.5);
8469                     y = 0;
8470                 break;
8471                 case "l":
8472                     x = 0;
8473                     y = r(h*.5);
8474                 break;
8475                 case "r":
8476                     x = w;
8477                     y = r(h*.5);
8478                 break;
8479                 case "b":
8480                     x = r(w*.5);
8481                     y = h;
8482                 break;
8483                 case "tl":
8484                     x = 0;
8485                     y = 0;
8486                 break;
8487                 case "bl":
8488                     x = 0;
8489                     y = h;
8490                 break;
8491                 case "br":
8492                     x = w;
8493                     y = h;
8494                 break;
8495                 case "tr":
8496                     x = w;
8497                     y = 0;
8498                 break;
8499             }
8500             if(local === true){
8501                 return [x, y];
8502             }
8503             if(vp){
8504                 var sc = this.getScroll();
8505                 return [x + sc.left, y + sc.top];
8506             }
8507             //Add the element's offset xy
8508             var o = this.getXY();
8509             return [x+o[0], y+o[1]];
8510         },
8511
8512         /**
8513          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8514          * supported position values.
8515          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8516          * @param {String} position The position to align to.
8517          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8518          * @return {Array} [x, y]
8519          */
8520         getAlignToXY : function(el, p, o){
8521             el = Roo.get(el);
8522             var d = this.dom;
8523             if(!el.dom){
8524                 throw "Element.alignTo with an element that doesn't exist";
8525             }
8526             var c = false; //constrain to viewport
8527             var p1 = "", p2 = "";
8528             o = o || [0,0];
8529
8530             if(!p){
8531                 p = "tl-bl";
8532             }else if(p == "?"){
8533                 p = "tl-bl?";
8534             }else if(p.indexOf("-") == -1){
8535                 p = "tl-" + p;
8536             }
8537             p = p.toLowerCase();
8538             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8539             if(!m){
8540                throw "Element.alignTo with an invalid alignment " + p;
8541             }
8542             p1 = m[1]; p2 = m[2]; c = !!m[3];
8543
8544             //Subtract the aligned el's internal xy from the target's offset xy
8545             //plus custom offset to get the aligned el's new offset xy
8546             var a1 = this.getAnchorXY(p1, true);
8547             var a2 = el.getAnchorXY(p2, false);
8548             var x = a2[0] - a1[0] + o[0];
8549             var y = a2[1] - a1[1] + o[1];
8550             if(c){
8551                 //constrain the aligned el to viewport if necessary
8552                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8553                 // 5px of margin for ie
8554                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8555
8556                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8557                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8558                 //otherwise swap the aligned el to the opposite border of the target.
8559                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8560                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8561                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8562                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8563
8564                var doc = document;
8565                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8566                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8567
8568                if((x+w) > dw + scrollX){
8569                     x = swapX ? r.left-w : dw+scrollX-w;
8570                 }
8571                if(x < scrollX){
8572                    x = swapX ? r.right : scrollX;
8573                }
8574                if((y+h) > dh + scrollY){
8575                     y = swapY ? r.top-h : dh+scrollY-h;
8576                 }
8577                if (y < scrollY){
8578                    y = swapY ? r.bottom : scrollY;
8579                }
8580             }
8581             return [x,y];
8582         },
8583
8584         // private
8585         getConstrainToXY : function(){
8586             var os = {top:0, left:0, bottom:0, right: 0};
8587
8588             return function(el, local, offsets, proposedXY){
8589                 el = Roo.get(el);
8590                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8591
8592                 var vw, vh, vx = 0, vy = 0;
8593                 if(el.dom == document.body || el.dom == document){
8594                     vw = Roo.lib.Dom.getViewWidth();
8595                     vh = Roo.lib.Dom.getViewHeight();
8596                 }else{
8597                     vw = el.dom.clientWidth;
8598                     vh = el.dom.clientHeight;
8599                     if(!local){
8600                         var vxy = el.getXY();
8601                         vx = vxy[0];
8602                         vy = vxy[1];
8603                     }
8604                 }
8605
8606                 var s = el.getScroll();
8607
8608                 vx += offsets.left + s.left;
8609                 vy += offsets.top + s.top;
8610
8611                 vw -= offsets.right;
8612                 vh -= offsets.bottom;
8613
8614                 var vr = vx+vw;
8615                 var vb = vy+vh;
8616
8617                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8618                 var x = xy[0], y = xy[1];
8619                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8620
8621                 // only move it if it needs it
8622                 var moved = false;
8623
8624                 // first validate right/bottom
8625                 if((x + w) > vr){
8626                     x = vr - w;
8627                     moved = true;
8628                 }
8629                 if((y + h) > vb){
8630                     y = vb - h;
8631                     moved = true;
8632                 }
8633                 // then make sure top/left isn't negative
8634                 if(x < vx){
8635                     x = vx;
8636                     moved = true;
8637                 }
8638                 if(y < vy){
8639                     y = vy;
8640                     moved = true;
8641                 }
8642                 return moved ? [x, y] : false;
8643             };
8644         }(),
8645
8646         // private
8647         adjustForConstraints : function(xy, parent, offsets){
8648             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8649         },
8650
8651         /**
8652          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8653          * document it aligns it to the viewport.
8654          * The position parameter is optional, and can be specified in any one of the following formats:
8655          * <ul>
8656          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8657          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8658          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8659          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8660          *   <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
8661          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8662          * </ul>
8663          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8664          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8665          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8666          * that specified in order to enforce the viewport constraints.
8667          * Following are all of the supported anchor positions:
8668     <pre>
8669     Value  Description
8670     -----  -----------------------------
8671     tl     The top left corner (default)
8672     t      The center of the top edge
8673     tr     The top right corner
8674     l      The center of the left edge
8675     c      In the center of the element
8676     r      The center of the right edge
8677     bl     The bottom left corner
8678     b      The center of the bottom edge
8679     br     The bottom right corner
8680     </pre>
8681     Example Usage:
8682     <pre><code>
8683     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8684     el.alignTo("other-el");
8685
8686     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8687     el.alignTo("other-el", "tr?");
8688
8689     // align the bottom right corner of el with the center left edge of other-el
8690     el.alignTo("other-el", "br-l?");
8691
8692     // align the center of el with the bottom left corner of other-el and
8693     // adjust the x position by -6 pixels (and the y position by 0)
8694     el.alignTo("other-el", "c-bl", [-6, 0]);
8695     </code></pre>
8696          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8697          * @param {String} position The position to align to.
8698          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8699          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8700          * @return {Roo.Element} this
8701          */
8702         alignTo : function(element, position, offsets, animate){
8703             var xy = this.getAlignToXY(element, position, offsets);
8704             this.setXY(xy, this.preanim(arguments, 3));
8705             return this;
8706         },
8707
8708         /**
8709          * Anchors an element to another element and realigns it when the window is resized.
8710          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8711          * @param {String} position The position to align to.
8712          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8713          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8714          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8715          * is a number, it is used as the buffer delay (defaults to 50ms).
8716          * @param {Function} callback The function to call after the animation finishes
8717          * @return {Roo.Element} this
8718          */
8719         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8720             var action = function(){
8721                 this.alignTo(el, alignment, offsets, animate);
8722                 Roo.callback(callback, this);
8723             };
8724             Roo.EventManager.onWindowResize(action, this);
8725             var tm = typeof monitorScroll;
8726             if(tm != 'undefined'){
8727                 Roo.EventManager.on(window, 'scroll', action, this,
8728                     {buffer: tm == 'number' ? monitorScroll : 50});
8729             }
8730             action.call(this); // align immediately
8731             return this;
8732         },
8733         /**
8734          * Clears any opacity settings from this element. Required in some cases for IE.
8735          * @return {Roo.Element} this
8736          */
8737         clearOpacity : function(){
8738             if (window.ActiveXObject) {
8739                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8740                     this.dom.style.filter = "";
8741                 }
8742             } else {
8743                 this.dom.style.opacity = "";
8744                 this.dom.style["-moz-opacity"] = "";
8745                 this.dom.style["-khtml-opacity"] = "";
8746             }
8747             return this;
8748         },
8749
8750         /**
8751          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8752          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8753          * @return {Roo.Element} this
8754          */
8755         hide : function(animate){
8756             this.setVisible(false, this.preanim(arguments, 0));
8757             return this;
8758         },
8759
8760         /**
8761         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8762         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8763          * @return {Roo.Element} this
8764          */
8765         show : function(animate){
8766             this.setVisible(true, this.preanim(arguments, 0));
8767             return this;
8768         },
8769
8770         /**
8771          * @private Test if size has a unit, otherwise appends the default
8772          */
8773         addUnits : function(size){
8774             return Roo.Element.addUnits(size, this.defaultUnit);
8775         },
8776
8777         /**
8778          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8779          * @return {Roo.Element} this
8780          */
8781         beginMeasure : function(){
8782             var el = this.dom;
8783             if(el.offsetWidth || el.offsetHeight){
8784                 return this; // offsets work already
8785             }
8786             var changed = [];
8787             var p = this.dom, b = document.body; // start with this element
8788             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8789                 var pe = Roo.get(p);
8790                 if(pe.getStyle('display') == 'none'){
8791                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8792                     p.style.visibility = "hidden";
8793                     p.style.display = "block";
8794                 }
8795                 p = p.parentNode;
8796             }
8797             this._measureChanged = changed;
8798             return this;
8799
8800         },
8801
8802         /**
8803          * Restores displays to before beginMeasure was called
8804          * @return {Roo.Element} this
8805          */
8806         endMeasure : function(){
8807             var changed = this._measureChanged;
8808             if(changed){
8809                 for(var i = 0, len = changed.length; i < len; i++) {
8810                     var r = changed[i];
8811                     r.el.style.visibility = r.visibility;
8812                     r.el.style.display = "none";
8813                 }
8814                 this._measureChanged = null;
8815             }
8816             return this;
8817         },
8818
8819         /**
8820         * Update the innerHTML of this element, optionally searching for and processing scripts
8821         * @param {String} html The new HTML
8822         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8823         * @param {Function} callback For async script loading you can be noticed when the update completes
8824         * @return {Roo.Element} this
8825          */
8826         update : function(html, loadScripts, callback){
8827             if(typeof html == "undefined"){
8828                 html = "";
8829             }
8830             if(loadScripts !== true){
8831                 this.dom.innerHTML = html;
8832                 if(typeof callback == "function"){
8833                     callback();
8834                 }
8835                 return this;
8836             }
8837             var id = Roo.id();
8838             var dom = this.dom;
8839
8840             html += '<span id="' + id + '"></span>';
8841
8842             E.onAvailable(id, function(){
8843                 var hd = document.getElementsByTagName("head")[0];
8844                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8845                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8846                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8847
8848                 var match;
8849                 while(match = re.exec(html)){
8850                     var attrs = match[1];
8851                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8852                     if(srcMatch && srcMatch[2]){
8853                        var s = document.createElement("script");
8854                        s.src = srcMatch[2];
8855                        var typeMatch = attrs.match(typeRe);
8856                        if(typeMatch && typeMatch[2]){
8857                            s.type = typeMatch[2];
8858                        }
8859                        hd.appendChild(s);
8860                     }else if(match[2] && match[2].length > 0){
8861                         if(window.execScript) {
8862                            window.execScript(match[2]);
8863                         } else {
8864                             /**
8865                              * eval:var:id
8866                              * eval:var:dom
8867                              * eval:var:html
8868                              * 
8869                              */
8870                            window.eval(match[2]);
8871                         }
8872                     }
8873                 }
8874                 var el = document.getElementById(id);
8875                 if(el){el.parentNode.removeChild(el);}
8876                 if(typeof callback == "function"){
8877                     callback();
8878                 }
8879             });
8880             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8881             return this;
8882         },
8883
8884         /**
8885          * Direct access to the UpdateManager update() method (takes the same parameters).
8886          * @param {String/Function} url The url for this request or a function to call to get the url
8887          * @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}
8888          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8889          * @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.
8890          * @return {Roo.Element} this
8891          */
8892         load : function(){
8893             var um = this.getUpdateManager();
8894             um.update.apply(um, arguments);
8895             return this;
8896         },
8897
8898         /**
8899         * Gets this element's UpdateManager
8900         * @return {Roo.UpdateManager} The UpdateManager
8901         */
8902         getUpdateManager : function(){
8903             if(!this.updateManager){
8904                 this.updateManager = new Roo.UpdateManager(this);
8905             }
8906             return this.updateManager;
8907         },
8908
8909         /**
8910          * Disables text selection for this element (normalized across browsers)
8911          * @return {Roo.Element} this
8912          */
8913         unselectable : function(){
8914             this.dom.unselectable = "on";
8915             this.swallowEvent("selectstart", true);
8916             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8917             this.addClass("x-unselectable");
8918             return this;
8919         },
8920
8921         /**
8922         * Calculates the x, y to center this element on the screen
8923         * @return {Array} The x, y values [x, y]
8924         */
8925         getCenterXY : function(){
8926             return this.getAlignToXY(document, 'c-c');
8927         },
8928
8929         /**
8930         * Centers the Element in either the viewport, or another Element.
8931         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8932         */
8933         center : function(centerIn){
8934             this.alignTo(centerIn || document, 'c-c');
8935             return this;
8936         },
8937
8938         /**
8939          * Tests various css rules/browsers to determine if this element uses a border box
8940          * @return {Boolean}
8941          */
8942         isBorderBox : function(){
8943             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8944         },
8945
8946         /**
8947          * Return a box {x, y, width, height} that can be used to set another elements
8948          * size/location to match this element.
8949          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8950          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8951          * @return {Object} box An object in the format {x, y, width, height}
8952          */
8953         getBox : function(contentBox, local){
8954             var xy;
8955             if(!local){
8956                 xy = this.getXY();
8957             }else{
8958                 var left = parseInt(this.getStyle("left"), 10) || 0;
8959                 var top = parseInt(this.getStyle("top"), 10) || 0;
8960                 xy = [left, top];
8961             }
8962             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8963             if(!contentBox){
8964                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8965             }else{
8966                 var l = this.getBorderWidth("l")+this.getPadding("l");
8967                 var r = this.getBorderWidth("r")+this.getPadding("r");
8968                 var t = this.getBorderWidth("t")+this.getPadding("t");
8969                 var b = this.getBorderWidth("b")+this.getPadding("b");
8970                 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)};
8971             }
8972             bx.right = bx.x + bx.width;
8973             bx.bottom = bx.y + bx.height;
8974             return bx;
8975         },
8976
8977         /**
8978          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8979          for more information about the sides.
8980          * @param {String} sides
8981          * @return {Number}
8982          */
8983         getFrameWidth : function(sides, onlyContentBox){
8984             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8985         },
8986
8987         /**
8988          * 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.
8989          * @param {Object} box The box to fill {x, y, width, height}
8990          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8991          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8992          * @return {Roo.Element} this
8993          */
8994         setBox : function(box, adjust, animate){
8995             var w = box.width, h = box.height;
8996             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8997                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8998                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8999             }
9000             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9001             return this;
9002         },
9003
9004         /**
9005          * Forces the browser to repaint this element
9006          * @return {Roo.Element} this
9007          */
9008          repaint : function(){
9009             var dom = this.dom;
9010             this.addClass("x-repaint");
9011             setTimeout(function(){
9012                 Roo.get(dom).removeClass("x-repaint");
9013             }, 1);
9014             return this;
9015         },
9016
9017         /**
9018          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9019          * then it returns the calculated width of the sides (see getPadding)
9020          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9021          * @return {Object/Number}
9022          */
9023         getMargins : function(side){
9024             if(!side){
9025                 return {
9026                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9027                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9028                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9029                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9030                 };
9031             }else{
9032                 return this.addStyles(side, El.margins);
9033              }
9034         },
9035
9036         // private
9037         addStyles : function(sides, styles){
9038             var val = 0, v, w;
9039             for(var i = 0, len = sides.length; i < len; i++){
9040                 v = this.getStyle(styles[sides.charAt(i)]);
9041                 if(v){
9042                      w = parseInt(v, 10);
9043                      if(w){ val += w; }
9044                 }
9045             }
9046             return val;
9047         },
9048
9049         /**
9050          * Creates a proxy element of this element
9051          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9052          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9053          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9054          * @return {Roo.Element} The new proxy element
9055          */
9056         createProxy : function(config, renderTo, matchBox){
9057             if(renderTo){
9058                 renderTo = Roo.getDom(renderTo);
9059             }else{
9060                 renderTo = document.body;
9061             }
9062             config = typeof config == "object" ?
9063                 config : {tag : "div", cls: config};
9064             var proxy = Roo.DomHelper.append(renderTo, config, true);
9065             if(matchBox){
9066                proxy.setBox(this.getBox());
9067             }
9068             return proxy;
9069         },
9070
9071         /**
9072          * Puts a mask over this element to disable user interaction. Requires core.css.
9073          * This method can only be applied to elements which accept child nodes.
9074          * @param {String} msg (optional) A message to display in the mask
9075          * @param {String} msgCls (optional) A css class to apply to the msg element
9076          * @return {Element} The mask  element
9077          */
9078         mask : function(msg, msgCls)
9079         {
9080             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9081                 this.setStyle("position", "relative");
9082             }
9083             if(!this._mask){
9084                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9085             }
9086             this.addClass("x-masked");
9087             this._mask.setDisplayed(true);
9088             
9089             // we wander
9090             var z = 0;
9091             var dom = this.dom;
9092             while (dom && dom.style) {
9093                 if (!isNaN(parseInt(dom.style.zIndex))) {
9094                     z = Math.max(z, parseInt(dom.style.zIndex));
9095                 }
9096                 dom = dom.parentNode;
9097             }
9098             // if we are masking the body - then it hides everything..
9099             if (this.dom == document.body) {
9100                 z = 1000000;
9101                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9102                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9103             }
9104            
9105             if(typeof msg == 'string'){
9106                 if(!this._maskMsg){
9107                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9108                 }
9109                 var mm = this._maskMsg;
9110                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9111                 if (mm.dom.firstChild) { // weird IE issue?
9112                     mm.dom.firstChild.innerHTML = msg;
9113                 }
9114                 mm.setDisplayed(true);
9115                 mm.center(this);
9116                 mm.setStyle('z-index', z + 102);
9117             }
9118             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9119                 this._mask.setHeight(this.getHeight());
9120             }
9121             this._mask.setStyle('z-index', z + 100);
9122             
9123             return this._mask;
9124         },
9125
9126         /**
9127          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9128          * it is cached for reuse.
9129          */
9130         unmask : function(removeEl){
9131             if(this._mask){
9132                 if(removeEl === true){
9133                     this._mask.remove();
9134                     delete this._mask;
9135                     if(this._maskMsg){
9136                         this._maskMsg.remove();
9137                         delete this._maskMsg;
9138                     }
9139                 }else{
9140                     this._mask.setDisplayed(false);
9141                     if(this._maskMsg){
9142                         this._maskMsg.setDisplayed(false);
9143                     }
9144                 }
9145             }
9146             this.removeClass("x-masked");
9147         },
9148
9149         /**
9150          * Returns true if this element is masked
9151          * @return {Boolean}
9152          */
9153         isMasked : function(){
9154             return this._mask && this._mask.isVisible();
9155         },
9156
9157         /**
9158          * Creates an iframe shim for this element to keep selects and other windowed objects from
9159          * showing through.
9160          * @return {Roo.Element} The new shim element
9161          */
9162         createShim : function(){
9163             var el = document.createElement('iframe');
9164             el.frameBorder = 'no';
9165             el.className = 'roo-shim';
9166             if(Roo.isIE && Roo.isSecure){
9167                 el.src = Roo.SSL_SECURE_URL;
9168             }
9169             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9170             shim.autoBoxAdjust = false;
9171             return shim;
9172         },
9173
9174         /**
9175          * Removes this element from the DOM and deletes it from the cache
9176          */
9177         remove : function(){
9178             if(this.dom.parentNode){
9179                 this.dom.parentNode.removeChild(this.dom);
9180             }
9181             delete El.cache[this.dom.id];
9182         },
9183
9184         /**
9185          * Sets up event handlers to add and remove a css class when the mouse is over this element
9186          * @param {String} className
9187          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9188          * mouseout events for children elements
9189          * @return {Roo.Element} this
9190          */
9191         addClassOnOver : function(className, preventFlicker){
9192             this.on("mouseover", function(){
9193                 Roo.fly(this, '_internal').addClass(className);
9194             }, this.dom);
9195             var removeFn = function(e){
9196                 if(preventFlicker !== true || !e.within(this, true)){
9197                     Roo.fly(this, '_internal').removeClass(className);
9198                 }
9199             };
9200             this.on("mouseout", removeFn, this.dom);
9201             return this;
9202         },
9203
9204         /**
9205          * Sets up event handlers to add and remove a css class when this element has the focus
9206          * @param {String} className
9207          * @return {Roo.Element} this
9208          */
9209         addClassOnFocus : function(className){
9210             this.on("focus", function(){
9211                 Roo.fly(this, '_internal').addClass(className);
9212             }, this.dom);
9213             this.on("blur", function(){
9214                 Roo.fly(this, '_internal').removeClass(className);
9215             }, this.dom);
9216             return this;
9217         },
9218         /**
9219          * 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)
9220          * @param {String} className
9221          * @return {Roo.Element} this
9222          */
9223         addClassOnClick : function(className){
9224             var dom = this.dom;
9225             this.on("mousedown", function(){
9226                 Roo.fly(dom, '_internal').addClass(className);
9227                 var d = Roo.get(document);
9228                 var fn = function(){
9229                     Roo.fly(dom, '_internal').removeClass(className);
9230                     d.removeListener("mouseup", fn);
9231                 };
9232                 d.on("mouseup", fn);
9233             });
9234             return this;
9235         },
9236
9237         /**
9238          * Stops the specified event from bubbling and optionally prevents the default action
9239          * @param {String} eventName
9240          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9241          * @return {Roo.Element} this
9242          */
9243         swallowEvent : function(eventName, preventDefault){
9244             var fn = function(e){
9245                 e.stopPropagation();
9246                 if(preventDefault){
9247                     e.preventDefault();
9248                 }
9249             };
9250             if(eventName instanceof Array){
9251                 for(var i = 0, len = eventName.length; i < len; i++){
9252                      this.on(eventName[i], fn);
9253                 }
9254                 return this;
9255             }
9256             this.on(eventName, fn);
9257             return this;
9258         },
9259
9260         /**
9261          * @private
9262          */
9263       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9264
9265         /**
9266          * Sizes this element to its parent element's dimensions performing
9267          * neccessary box adjustments.
9268          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9269          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9270          * @return {Roo.Element} this
9271          */
9272         fitToParent : function(monitorResize, targetParent) {
9273           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9274           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9275           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9276             return;
9277           }
9278           var p = Roo.get(targetParent || this.dom.parentNode);
9279           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9280           if (monitorResize === true) {
9281             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9282             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9283           }
9284           return this;
9285         },
9286
9287         /**
9288          * Gets the next sibling, skipping text nodes
9289          * @return {HTMLElement} The next sibling or null
9290          */
9291         getNextSibling : function(){
9292             var n = this.dom.nextSibling;
9293             while(n && n.nodeType != 1){
9294                 n = n.nextSibling;
9295             }
9296             return n;
9297         },
9298
9299         /**
9300          * Gets the previous sibling, skipping text nodes
9301          * @return {HTMLElement} The previous sibling or null
9302          */
9303         getPrevSibling : function(){
9304             var n = this.dom.previousSibling;
9305             while(n && n.nodeType != 1){
9306                 n = n.previousSibling;
9307             }
9308             return n;
9309         },
9310
9311
9312         /**
9313          * Appends the passed element(s) to this element
9314          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9315          * @return {Roo.Element} this
9316          */
9317         appendChild: function(el){
9318             el = Roo.get(el);
9319             el.appendTo(this);
9320             return this;
9321         },
9322
9323         /**
9324          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9325          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9326          * automatically generated with the specified attributes.
9327          * @param {HTMLElement} insertBefore (optional) a child element of this element
9328          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9329          * @return {Roo.Element} The new child element
9330          */
9331         createChild: function(config, insertBefore, returnDom){
9332             config = config || {tag:'div'};
9333             if(insertBefore){
9334                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9335             }
9336             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9337         },
9338
9339         /**
9340          * Appends this element to the passed element
9341          * @param {String/HTMLElement/Element} el The new parent element
9342          * @return {Roo.Element} this
9343          */
9344         appendTo: function(el){
9345             el = Roo.getDom(el);
9346             el.appendChild(this.dom);
9347             return this;
9348         },
9349
9350         /**
9351          * Inserts this element before the passed element in the DOM
9352          * @param {String/HTMLElement/Element} el The element to insert before
9353          * @return {Roo.Element} this
9354          */
9355         insertBefore: function(el){
9356             el = Roo.getDom(el);
9357             el.parentNode.insertBefore(this.dom, el);
9358             return this;
9359         },
9360
9361         /**
9362          * Inserts this element after the passed element in the DOM
9363          * @param {String/HTMLElement/Element} el The element to insert after
9364          * @return {Roo.Element} this
9365          */
9366         insertAfter: function(el){
9367             el = Roo.getDom(el);
9368             el.parentNode.insertBefore(this.dom, el.nextSibling);
9369             return this;
9370         },
9371
9372         /**
9373          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9374          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9375          * @return {Roo.Element} The new child
9376          */
9377         insertFirst: function(el, returnDom){
9378             el = el || {};
9379             if(typeof el == 'object' && !el.nodeType){ // dh config
9380                 return this.createChild(el, this.dom.firstChild, returnDom);
9381             }else{
9382                 el = Roo.getDom(el);
9383                 this.dom.insertBefore(el, this.dom.firstChild);
9384                 return !returnDom ? Roo.get(el) : el;
9385             }
9386         },
9387
9388         /**
9389          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9390          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9391          * @param {String} where (optional) 'before' or 'after' defaults to before
9392          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9393          * @return {Roo.Element} the inserted Element
9394          */
9395         insertSibling: function(el, where, returnDom){
9396             where = where ? where.toLowerCase() : 'before';
9397             el = el || {};
9398             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9399
9400             if(typeof el == 'object' && !el.nodeType){ // dh config
9401                 if(where == 'after' && !this.dom.nextSibling){
9402                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9403                 }else{
9404                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9405                 }
9406
9407             }else{
9408                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9409                             where == 'before' ? this.dom : this.dom.nextSibling);
9410                 if(!returnDom){
9411                     rt = Roo.get(rt);
9412                 }
9413             }
9414             return rt;
9415         },
9416
9417         /**
9418          * Creates and wraps this element with another element
9419          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9420          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9421          * @return {HTMLElement/Element} The newly created wrapper element
9422          */
9423         wrap: function(config, returnDom){
9424             if(!config){
9425                 config = {tag: "div"};
9426             }
9427             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9428             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9429             return newEl;
9430         },
9431
9432         /**
9433          * Replaces the passed element with this element
9434          * @param {String/HTMLElement/Element} el The element to replace
9435          * @return {Roo.Element} this
9436          */
9437         replace: function(el){
9438             el = Roo.get(el);
9439             this.insertBefore(el);
9440             el.remove();
9441             return this;
9442         },
9443
9444         /**
9445          * Inserts an html fragment into this element
9446          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9447          * @param {String} html The HTML fragment
9448          * @param {Boolean} returnEl True to return an Roo.Element
9449          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9450          */
9451         insertHtml : function(where, html, returnEl){
9452             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9453             return returnEl ? Roo.get(el) : el;
9454         },
9455
9456         /**
9457          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9458          * @param {Object} o The object with the attributes
9459          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9460          * @return {Roo.Element} this
9461          */
9462         set : function(o, useSet){
9463             var el = this.dom;
9464             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9465             for(var attr in o){
9466                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9467                 if(attr=="cls"){
9468                     el.className = o["cls"];
9469                 }else{
9470                     if(useSet) {
9471                         el.setAttribute(attr, o[attr]);
9472                     } else {
9473                         el[attr] = o[attr];
9474                     }
9475                 }
9476             }
9477             if(o.style){
9478                 Roo.DomHelper.applyStyles(el, o.style);
9479             }
9480             return this;
9481         },
9482
9483         /**
9484          * Convenience method for constructing a KeyMap
9485          * @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:
9486          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9487          * @param {Function} fn The function to call
9488          * @param {Object} scope (optional) The scope of the function
9489          * @return {Roo.KeyMap} The KeyMap created
9490          */
9491         addKeyListener : function(key, fn, scope){
9492             var config;
9493             if(typeof key != "object" || key instanceof Array){
9494                 config = {
9495                     key: key,
9496                     fn: fn,
9497                     scope: scope
9498                 };
9499             }else{
9500                 config = {
9501                     key : key.key,
9502                     shift : key.shift,
9503                     ctrl : key.ctrl,
9504                     alt : key.alt,
9505                     fn: fn,
9506                     scope: scope
9507                 };
9508             }
9509             return new Roo.KeyMap(this, config);
9510         },
9511
9512         /**
9513          * Creates a KeyMap for this element
9514          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9515          * @return {Roo.KeyMap} The KeyMap created
9516          */
9517         addKeyMap : function(config){
9518             return new Roo.KeyMap(this, config);
9519         },
9520
9521         /**
9522          * Returns true if this element is scrollable.
9523          * @return {Boolean}
9524          */
9525          isScrollable : function(){
9526             var dom = this.dom;
9527             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9528         },
9529
9530         /**
9531          * 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().
9532          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9533          * @param {Number} value The new scroll value
9534          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9535          * @return {Element} this
9536          */
9537
9538         scrollTo : function(side, value, animate){
9539             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9540             if(!animate || !A){
9541                 this.dom[prop] = value;
9542             }else{
9543                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9544                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9545             }
9546             return this;
9547         },
9548
9549         /**
9550          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9551          * within this element's scrollable range.
9552          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9553          * @param {Number} distance How far to scroll the element in pixels
9554          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9555          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9556          * was scrolled as far as it could go.
9557          */
9558          scroll : function(direction, distance, animate){
9559              if(!this.isScrollable()){
9560                  return;
9561              }
9562              var el = this.dom;
9563              var l = el.scrollLeft, t = el.scrollTop;
9564              var w = el.scrollWidth, h = el.scrollHeight;
9565              var cw = el.clientWidth, ch = el.clientHeight;
9566              direction = direction.toLowerCase();
9567              var scrolled = false;
9568              var a = this.preanim(arguments, 2);
9569              switch(direction){
9570                  case "l":
9571                  case "left":
9572                      if(w - l > cw){
9573                          var v = Math.min(l + distance, w-cw);
9574                          this.scrollTo("left", v, a);
9575                          scrolled = true;
9576                      }
9577                      break;
9578                 case "r":
9579                 case "right":
9580                      if(l > 0){
9581                          var v = Math.max(l - distance, 0);
9582                          this.scrollTo("left", v, a);
9583                          scrolled = true;
9584                      }
9585                      break;
9586                 case "t":
9587                 case "top":
9588                 case "up":
9589                      if(t > 0){
9590                          var v = Math.max(t - distance, 0);
9591                          this.scrollTo("top", v, a);
9592                          scrolled = true;
9593                      }
9594                      break;
9595                 case "b":
9596                 case "bottom":
9597                 case "down":
9598                      if(h - t > ch){
9599                          var v = Math.min(t + distance, h-ch);
9600                          this.scrollTo("top", v, a);
9601                          scrolled = true;
9602                      }
9603                      break;
9604              }
9605              return scrolled;
9606         },
9607
9608         /**
9609          * Translates the passed page coordinates into left/top css values for this element
9610          * @param {Number/Array} x The page x or an array containing [x, y]
9611          * @param {Number} y The page y
9612          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9613          */
9614         translatePoints : function(x, y){
9615             if(typeof x == 'object' || x instanceof Array){
9616                 y = x[1]; x = x[0];
9617             }
9618             var p = this.getStyle('position');
9619             var o = this.getXY();
9620
9621             var l = parseInt(this.getStyle('left'), 10);
9622             var t = parseInt(this.getStyle('top'), 10);
9623
9624             if(isNaN(l)){
9625                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9626             }
9627             if(isNaN(t)){
9628                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9629             }
9630
9631             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9632         },
9633
9634         /**
9635          * Returns the current scroll position of the element.
9636          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9637          */
9638         getScroll : function(){
9639             var d = this.dom, doc = document;
9640             if(d == doc || d == doc.body){
9641                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9642                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9643                 return {left: l, top: t};
9644             }else{
9645                 return {left: d.scrollLeft, top: d.scrollTop};
9646             }
9647         },
9648
9649         /**
9650          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9651          * are convert to standard 6 digit hex color.
9652          * @param {String} attr The css attribute
9653          * @param {String} defaultValue The default value to use when a valid color isn't found
9654          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9655          * YUI color anims.
9656          */
9657         getColor : function(attr, defaultValue, prefix){
9658             var v = this.getStyle(attr);
9659             if(!v || v == "transparent" || v == "inherit") {
9660                 return defaultValue;
9661             }
9662             var color = typeof prefix == "undefined" ? "#" : prefix;
9663             if(v.substr(0, 4) == "rgb("){
9664                 var rvs = v.slice(4, v.length -1).split(",");
9665                 for(var i = 0; i < 3; i++){
9666                     var h = parseInt(rvs[i]).toString(16);
9667                     if(h < 16){
9668                         h = "0" + h;
9669                     }
9670                     color += h;
9671                 }
9672             } else {
9673                 if(v.substr(0, 1) == "#"){
9674                     if(v.length == 4) {
9675                         for(var i = 1; i < 4; i++){
9676                             var c = v.charAt(i);
9677                             color +=  c + c;
9678                         }
9679                     }else if(v.length == 7){
9680                         color += v.substr(1);
9681                     }
9682                 }
9683             }
9684             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9685         },
9686
9687         /**
9688          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9689          * gradient background, rounded corners and a 4-way shadow.
9690          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9691          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9692          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9693          * @return {Roo.Element} this
9694          */
9695         boxWrap : function(cls){
9696             cls = cls || 'x-box';
9697             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9698             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9699             return el;
9700         },
9701
9702         /**
9703          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9704          * @param {String} namespace The namespace in which to look for the attribute
9705          * @param {String} name The attribute name
9706          * @return {String} The attribute value
9707          */
9708         getAttributeNS : Roo.isIE ? function(ns, name){
9709             var d = this.dom;
9710             var type = typeof d[ns+":"+name];
9711             if(type != 'undefined' && type != 'unknown'){
9712                 return d[ns+":"+name];
9713             }
9714             return d[name];
9715         } : function(ns, name){
9716             var d = this.dom;
9717             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9718         },
9719         
9720         
9721         /**
9722          * Sets or Returns the value the dom attribute value
9723          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9724          * @param {String} value (optional) The value to set the attribute to
9725          * @return {String} The attribute value
9726          */
9727         attr : function(name){
9728             if (arguments.length > 1) {
9729                 this.dom.setAttribute(name, arguments[1]);
9730                 return arguments[1];
9731             }
9732             if (typeof(name) == 'object') {
9733                 for(var i in name) {
9734                     this.attr(i, name[i]);
9735                 }
9736                 return name;
9737             }
9738             
9739             
9740             if (!this.dom.hasAttribute(name)) {
9741                 return undefined;
9742             }
9743             return this.dom.getAttribute(name);
9744         }
9745         
9746         
9747         
9748     };
9749
9750     var ep = El.prototype;
9751
9752     /**
9753      * Appends an event handler (Shorthand for addListener)
9754      * @param {String}   eventName     The type of event to append
9755      * @param {Function} fn        The method the event invokes
9756      * @param {Object} scope       (optional) The scope (this object) of the fn
9757      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9758      * @method
9759      */
9760     ep.on = ep.addListener;
9761         // backwards compat
9762     ep.mon = ep.addListener;
9763
9764     /**
9765      * Removes an event handler from this element (shorthand for removeListener)
9766      * @param {String} eventName the type of event to remove
9767      * @param {Function} fn the method the event invokes
9768      * @return {Roo.Element} this
9769      * @method
9770      */
9771     ep.un = ep.removeListener;
9772
9773     /**
9774      * true to automatically adjust width and height settings for box-model issues (default to true)
9775      */
9776     ep.autoBoxAdjust = true;
9777
9778     // private
9779     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9780
9781     // private
9782     El.addUnits = function(v, defaultUnit){
9783         if(v === "" || v == "auto"){
9784             return v;
9785         }
9786         if(v === undefined){
9787             return '';
9788         }
9789         if(typeof v == "number" || !El.unitPattern.test(v)){
9790             return v + (defaultUnit || 'px');
9791         }
9792         return v;
9793     };
9794
9795     // special markup used throughout Roo when box wrapping elements
9796     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>';
9797     /**
9798      * Visibility mode constant - Use visibility to hide element
9799      * @static
9800      * @type Number
9801      */
9802     El.VISIBILITY = 1;
9803     /**
9804      * Visibility mode constant - Use display to hide element
9805      * @static
9806      * @type Number
9807      */
9808     El.DISPLAY = 2;
9809
9810     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9811     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9812     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9813
9814
9815
9816     /**
9817      * @private
9818      */
9819     El.cache = {};
9820
9821     var docEl;
9822
9823     /**
9824      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9825      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9826      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9827      * @return {Element} The Element object
9828      * @static
9829      */
9830     El.get = function(el){
9831         var ex, elm, id;
9832         if(!el){ return null; }
9833         if(typeof el == "string"){ // element id
9834             if(!(elm = document.getElementById(el))){
9835                 return null;
9836             }
9837             if(ex = El.cache[el]){
9838                 ex.dom = elm;
9839             }else{
9840                 ex = El.cache[el] = new El(elm);
9841             }
9842             return ex;
9843         }else if(el.tagName){ // dom element
9844             if(!(id = el.id)){
9845                 id = Roo.id(el);
9846             }
9847             if(ex = El.cache[id]){
9848                 ex.dom = el;
9849             }else{
9850                 ex = El.cache[id] = new El(el);
9851             }
9852             return ex;
9853         }else if(el instanceof El){
9854             if(el != docEl){
9855                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9856                                                               // catch case where it hasn't been appended
9857                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9858             }
9859             return el;
9860         }else if(el.isComposite){
9861             return el;
9862         }else if(el instanceof Array){
9863             return El.select(el);
9864         }else if(el == document){
9865             // create a bogus element object representing the document object
9866             if(!docEl){
9867                 var f = function(){};
9868                 f.prototype = El.prototype;
9869                 docEl = new f();
9870                 docEl.dom = document;
9871             }
9872             return docEl;
9873         }
9874         return null;
9875     };
9876
9877     // private
9878     El.uncache = function(el){
9879         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9880             if(a[i]){
9881                 delete El.cache[a[i].id || a[i]];
9882             }
9883         }
9884     };
9885
9886     // private
9887     // Garbage collection - uncache elements/purge listeners on orphaned elements
9888     // so we don't hold a reference and cause the browser to retain them
9889     El.garbageCollect = function(){
9890         if(!Roo.enableGarbageCollector){
9891             clearInterval(El.collectorThread);
9892             return;
9893         }
9894         for(var eid in El.cache){
9895             var el = El.cache[eid], d = el.dom;
9896             // -------------------------------------------------------
9897             // Determining what is garbage:
9898             // -------------------------------------------------------
9899             // !d
9900             // dom node is null, definitely garbage
9901             // -------------------------------------------------------
9902             // !d.parentNode
9903             // no parentNode == direct orphan, definitely garbage
9904             // -------------------------------------------------------
9905             // !d.offsetParent && !document.getElementById(eid)
9906             // display none elements have no offsetParent so we will
9907             // also try to look it up by it's id. However, check
9908             // offsetParent first so we don't do unneeded lookups.
9909             // This enables collection of elements that are not orphans
9910             // directly, but somewhere up the line they have an orphan
9911             // parent.
9912             // -------------------------------------------------------
9913             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9914                 delete El.cache[eid];
9915                 if(d && Roo.enableListenerCollection){
9916                     E.purgeElement(d);
9917                 }
9918             }
9919         }
9920     }
9921     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9922
9923
9924     // dom is optional
9925     El.Flyweight = function(dom){
9926         this.dom = dom;
9927     };
9928     El.Flyweight.prototype = El.prototype;
9929
9930     El._flyweights = {};
9931     /**
9932      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9933      * the dom node can be overwritten by other code.
9934      * @param {String/HTMLElement} el The dom node or id
9935      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9936      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9937      * @static
9938      * @return {Element} The shared Element object
9939      */
9940     El.fly = function(el, named){
9941         named = named || '_global';
9942         el = Roo.getDom(el);
9943         if(!el){
9944             return null;
9945         }
9946         if(!El._flyweights[named]){
9947             El._flyweights[named] = new El.Flyweight();
9948         }
9949         El._flyweights[named].dom = el;
9950         return El._flyweights[named];
9951     };
9952
9953     /**
9954      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9955      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9956      * Shorthand of {@link Roo.Element#get}
9957      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9958      * @return {Element} The Element object
9959      * @member Roo
9960      * @method get
9961      */
9962     Roo.get = El.get;
9963     /**
9964      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9965      * the dom node can be overwritten by other code.
9966      * Shorthand of {@link Roo.Element#fly}
9967      * @param {String/HTMLElement} el The dom node or id
9968      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9969      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9970      * @static
9971      * @return {Element} The shared Element object
9972      * @member Roo
9973      * @method fly
9974      */
9975     Roo.fly = El.fly;
9976
9977     // speedy lookup for elements never to box adjust
9978     var noBoxAdjust = Roo.isStrict ? {
9979         select:1
9980     } : {
9981         input:1, select:1, textarea:1
9982     };
9983     if(Roo.isIE || Roo.isGecko){
9984         noBoxAdjust['button'] = 1;
9985     }
9986
9987
9988     Roo.EventManager.on(window, 'unload', function(){
9989         delete El.cache;
9990         delete El._flyweights;
9991     });
9992 })();
9993
9994
9995
9996
9997 if(Roo.DomQuery){
9998     Roo.Element.selectorFunction = Roo.DomQuery.select;
9999 }
10000
10001 Roo.Element.select = function(selector, unique, root){
10002     var els;
10003     if(typeof selector == "string"){
10004         els = Roo.Element.selectorFunction(selector, root);
10005     }else if(selector.length !== undefined){
10006         els = selector;
10007     }else{
10008         throw "Invalid selector";
10009     }
10010     if(unique === true){
10011         return new Roo.CompositeElement(els);
10012     }else{
10013         return new Roo.CompositeElementLite(els);
10014     }
10015 };
10016 /**
10017  * Selects elements based on the passed CSS selector to enable working on them as 1.
10018  * @param {String/Array} selector The CSS selector or an array of elements
10019  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10020  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10021  * @return {CompositeElementLite/CompositeElement}
10022  * @member Roo
10023  * @method select
10024  */
10025 Roo.select = Roo.Element.select;
10026
10027
10028
10029
10030
10031
10032
10033
10034
10035
10036
10037
10038
10039
10040 /*
10041  * Based on:
10042  * Ext JS Library 1.1.1
10043  * Copyright(c) 2006-2007, Ext JS, LLC.
10044  *
10045  * Originally Released Under LGPL - original licence link has changed is not relivant.
10046  *
10047  * Fork - LGPL
10048  * <script type="text/javascript">
10049  */
10050
10051
10052
10053 //Notifies Element that fx methods are available
10054 Roo.enableFx = true;
10055
10056 /**
10057  * @class Roo.Fx
10058  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10059  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10060  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10061  * Element effects to work.</p><br/>
10062  *
10063  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10064  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10065  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10066  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10067  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10068  * expected results and should be done with care.</p><br/>
10069  *
10070  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10071  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10072 <pre>
10073 Value  Description
10074 -----  -----------------------------
10075 tl     The top left corner
10076 t      The center of the top edge
10077 tr     The top right corner
10078 l      The center of the left edge
10079 r      The center of the right edge
10080 bl     The bottom left corner
10081 b      The center of the bottom edge
10082 br     The bottom right corner
10083 </pre>
10084  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10085  * below are common options that can be passed to any Fx method.</b>
10086  * @cfg {Function} callback A function called when the effect is finished
10087  * @cfg {Object} scope The scope of the effect function
10088  * @cfg {String} easing A valid Easing value for the effect
10089  * @cfg {String} afterCls A css class to apply after the effect
10090  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10091  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10092  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10093  * effects that end with the element being visually hidden, ignored otherwise)
10094  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10095  * a function which returns such a specification that will be applied to the Element after the effect finishes
10096  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10097  * @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
10098  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10099  */
10100 Roo.Fx = {
10101         /**
10102          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10103          * origin for the slide effect.  This function automatically handles wrapping the element with
10104          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10105          * Usage:
10106          *<pre><code>
10107 // default: slide the element in from the top
10108 el.slideIn();
10109
10110 // custom: slide the element in from the right with a 2-second duration
10111 el.slideIn('r', { duration: 2 });
10112
10113 // common config options shown with default values
10114 el.slideIn('t', {
10115     easing: 'easeOut',
10116     duration: .5
10117 });
10118 </code></pre>
10119          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10120          * @param {Object} options (optional) Object literal with any of the Fx config options
10121          * @return {Roo.Element} The Element
10122          */
10123     slideIn : function(anchor, o){
10124         var el = this.getFxEl();
10125         o = o || {};
10126
10127         el.queueFx(o, function(){
10128
10129             anchor = anchor || "t";
10130
10131             // fix display to visibility
10132             this.fixDisplay();
10133
10134             // restore values after effect
10135             var r = this.getFxRestore();
10136             var b = this.getBox();
10137             // fixed size for slide
10138             this.setSize(b);
10139
10140             // wrap if needed
10141             var wrap = this.fxWrap(r.pos, o, "hidden");
10142
10143             var st = this.dom.style;
10144             st.visibility = "visible";
10145             st.position = "absolute";
10146
10147             // clear out temp styles after slide and unwrap
10148             var after = function(){
10149                 el.fxUnwrap(wrap, r.pos, o);
10150                 st.width = r.width;
10151                 st.height = r.height;
10152                 el.afterFx(o);
10153             };
10154             // time to calc the positions
10155             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10156
10157             switch(anchor.toLowerCase()){
10158                 case "t":
10159                     wrap.setSize(b.width, 0);
10160                     st.left = st.bottom = "0";
10161                     a = {height: bh};
10162                 break;
10163                 case "l":
10164                     wrap.setSize(0, b.height);
10165                     st.right = st.top = "0";
10166                     a = {width: bw};
10167                 break;
10168                 case "r":
10169                     wrap.setSize(0, b.height);
10170                     wrap.setX(b.right);
10171                     st.left = st.top = "0";
10172                     a = {width: bw, points: pt};
10173                 break;
10174                 case "b":
10175                     wrap.setSize(b.width, 0);
10176                     wrap.setY(b.bottom);
10177                     st.left = st.top = "0";
10178                     a = {height: bh, points: pt};
10179                 break;
10180                 case "tl":
10181                     wrap.setSize(0, 0);
10182                     st.right = st.bottom = "0";
10183                     a = {width: bw, height: bh};
10184                 break;
10185                 case "bl":
10186                     wrap.setSize(0, 0);
10187                     wrap.setY(b.y+b.height);
10188                     st.right = st.top = "0";
10189                     a = {width: bw, height: bh, points: pt};
10190                 break;
10191                 case "br":
10192                     wrap.setSize(0, 0);
10193                     wrap.setXY([b.right, b.bottom]);
10194                     st.left = st.top = "0";
10195                     a = {width: bw, height: bh, points: pt};
10196                 break;
10197                 case "tr":
10198                     wrap.setSize(0, 0);
10199                     wrap.setX(b.x+b.width);
10200                     st.left = st.bottom = "0";
10201                     a = {width: bw, height: bh, points: pt};
10202                 break;
10203             }
10204             this.dom.style.visibility = "visible";
10205             wrap.show();
10206
10207             arguments.callee.anim = wrap.fxanim(a,
10208                 o,
10209                 'motion',
10210                 .5,
10211                 'easeOut', after);
10212         });
10213         return this;
10214     },
10215     
10216         /**
10217          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10218          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10219          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10220          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10221          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10222          * Usage:
10223          *<pre><code>
10224 // default: slide the element out to the top
10225 el.slideOut();
10226
10227 // custom: slide the element out to the right with a 2-second duration
10228 el.slideOut('r', { duration: 2 });
10229
10230 // common config options shown with default values
10231 el.slideOut('t', {
10232     easing: 'easeOut',
10233     duration: .5,
10234     remove: false,
10235     useDisplay: false
10236 });
10237 </code></pre>
10238          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10239          * @param {Object} options (optional) Object literal with any of the Fx config options
10240          * @return {Roo.Element} The Element
10241          */
10242     slideOut : function(anchor, o){
10243         var el = this.getFxEl();
10244         o = o || {};
10245
10246         el.queueFx(o, function(){
10247
10248             anchor = anchor || "t";
10249
10250             // restore values after effect
10251             var r = this.getFxRestore();
10252             
10253             var b = this.getBox();
10254             // fixed size for slide
10255             this.setSize(b);
10256
10257             // wrap if needed
10258             var wrap = this.fxWrap(r.pos, o, "visible");
10259
10260             var st = this.dom.style;
10261             st.visibility = "visible";
10262             st.position = "absolute";
10263
10264             wrap.setSize(b);
10265
10266             var after = function(){
10267                 if(o.useDisplay){
10268                     el.setDisplayed(false);
10269                 }else{
10270                     el.hide();
10271                 }
10272
10273                 el.fxUnwrap(wrap, r.pos, o);
10274
10275                 st.width = r.width;
10276                 st.height = r.height;
10277
10278                 el.afterFx(o);
10279             };
10280
10281             var a, zero = {to: 0};
10282             switch(anchor.toLowerCase()){
10283                 case "t":
10284                     st.left = st.bottom = "0";
10285                     a = {height: zero};
10286                 break;
10287                 case "l":
10288                     st.right = st.top = "0";
10289                     a = {width: zero};
10290                 break;
10291                 case "r":
10292                     st.left = st.top = "0";
10293                     a = {width: zero, points: {to:[b.right, b.y]}};
10294                 break;
10295                 case "b":
10296                     st.left = st.top = "0";
10297                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10298                 break;
10299                 case "tl":
10300                     st.right = st.bottom = "0";
10301                     a = {width: zero, height: zero};
10302                 break;
10303                 case "bl":
10304                     st.right = st.top = "0";
10305                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10306                 break;
10307                 case "br":
10308                     st.left = st.top = "0";
10309                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10310                 break;
10311                 case "tr":
10312                     st.left = st.bottom = "0";
10313                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10314                 break;
10315             }
10316
10317             arguments.callee.anim = wrap.fxanim(a,
10318                 o,
10319                 'motion',
10320                 .5,
10321                 "easeOut", after);
10322         });
10323         return this;
10324     },
10325
10326         /**
10327          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10328          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10329          * The element must be removed from the DOM using the 'remove' config option if desired.
10330          * Usage:
10331          *<pre><code>
10332 // default
10333 el.puff();
10334
10335 // common config options shown with default values
10336 el.puff({
10337     easing: 'easeOut',
10338     duration: .5,
10339     remove: false,
10340     useDisplay: false
10341 });
10342 </code></pre>
10343          * @param {Object} options (optional) Object literal with any of the Fx config options
10344          * @return {Roo.Element} The Element
10345          */
10346     puff : function(o){
10347         var el = this.getFxEl();
10348         o = o || {};
10349
10350         el.queueFx(o, function(){
10351             this.clearOpacity();
10352             this.show();
10353
10354             // restore values after effect
10355             var r = this.getFxRestore();
10356             var st = this.dom.style;
10357
10358             var after = function(){
10359                 if(o.useDisplay){
10360                     el.setDisplayed(false);
10361                 }else{
10362                     el.hide();
10363                 }
10364
10365                 el.clearOpacity();
10366
10367                 el.setPositioning(r.pos);
10368                 st.width = r.width;
10369                 st.height = r.height;
10370                 st.fontSize = '';
10371                 el.afterFx(o);
10372             };
10373
10374             var width = this.getWidth();
10375             var height = this.getHeight();
10376
10377             arguments.callee.anim = this.fxanim({
10378                     width : {to: this.adjustWidth(width * 2)},
10379                     height : {to: this.adjustHeight(height * 2)},
10380                     points : {by: [-(width * .5), -(height * .5)]},
10381                     opacity : {to: 0},
10382                     fontSize: {to:200, unit: "%"}
10383                 },
10384                 o,
10385                 'motion',
10386                 .5,
10387                 "easeOut", after);
10388         });
10389         return this;
10390     },
10391
10392         /**
10393          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10394          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10395          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10396          * Usage:
10397          *<pre><code>
10398 // default
10399 el.switchOff();
10400
10401 // all config options shown with default values
10402 el.switchOff({
10403     easing: 'easeIn',
10404     duration: .3,
10405     remove: false,
10406     useDisplay: false
10407 });
10408 </code></pre>
10409          * @param {Object} options (optional) Object literal with any of the Fx config options
10410          * @return {Roo.Element} The Element
10411          */
10412     switchOff : function(o){
10413         var el = this.getFxEl();
10414         o = o || {};
10415
10416         el.queueFx(o, function(){
10417             this.clearOpacity();
10418             this.clip();
10419
10420             // restore values after effect
10421             var r = this.getFxRestore();
10422             var st = this.dom.style;
10423
10424             var after = function(){
10425                 if(o.useDisplay){
10426                     el.setDisplayed(false);
10427                 }else{
10428                     el.hide();
10429                 }
10430
10431                 el.clearOpacity();
10432                 el.setPositioning(r.pos);
10433                 st.width = r.width;
10434                 st.height = r.height;
10435
10436                 el.afterFx(o);
10437             };
10438
10439             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10440                 this.clearOpacity();
10441                 (function(){
10442                     this.fxanim({
10443                         height:{to:1},
10444                         points:{by:[0, this.getHeight() * .5]}
10445                     }, o, 'motion', 0.3, 'easeIn', after);
10446                 }).defer(100, this);
10447             });
10448         });
10449         return this;
10450     },
10451
10452     /**
10453      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10454      * changed using the "attr" config option) and then fading back to the original color. If no original
10455      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10456      * Usage:
10457 <pre><code>
10458 // default: highlight background to yellow
10459 el.highlight();
10460
10461 // custom: highlight foreground text to blue for 2 seconds
10462 el.highlight("0000ff", { attr: 'color', duration: 2 });
10463
10464 // common config options shown with default values
10465 el.highlight("ffff9c", {
10466     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10467     endColor: (current color) or "ffffff",
10468     easing: 'easeIn',
10469     duration: 1
10470 });
10471 </code></pre>
10472      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10473      * @param {Object} options (optional) Object literal with any of the Fx config options
10474      * @return {Roo.Element} The Element
10475      */ 
10476     highlight : function(color, o){
10477         var el = this.getFxEl();
10478         o = o || {};
10479
10480         el.queueFx(o, function(){
10481             color = color || "ffff9c";
10482             attr = o.attr || "backgroundColor";
10483
10484             this.clearOpacity();
10485             this.show();
10486
10487             var origColor = this.getColor(attr);
10488             var restoreColor = this.dom.style[attr];
10489             endColor = (o.endColor || origColor) || "ffffff";
10490
10491             var after = function(){
10492                 el.dom.style[attr] = restoreColor;
10493                 el.afterFx(o);
10494             };
10495
10496             var a = {};
10497             a[attr] = {from: color, to: endColor};
10498             arguments.callee.anim = this.fxanim(a,
10499                 o,
10500                 'color',
10501                 1,
10502                 'easeIn', after);
10503         });
10504         return this;
10505     },
10506
10507    /**
10508     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10509     * Usage:
10510 <pre><code>
10511 // default: a single light blue ripple
10512 el.frame();
10513
10514 // custom: 3 red ripples lasting 3 seconds total
10515 el.frame("ff0000", 3, { duration: 3 });
10516
10517 // common config options shown with default values
10518 el.frame("C3DAF9", 1, {
10519     duration: 1 //duration of entire animation (not each individual ripple)
10520     // Note: Easing is not configurable and will be ignored if included
10521 });
10522 </code></pre>
10523     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10524     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10525     * @param {Object} options (optional) Object literal with any of the Fx config options
10526     * @return {Roo.Element} The Element
10527     */
10528     frame : function(color, count, o){
10529         var el = this.getFxEl();
10530         o = o || {};
10531
10532         el.queueFx(o, function(){
10533             color = color || "#C3DAF9";
10534             if(color.length == 6){
10535                 color = "#" + color;
10536             }
10537             count = count || 1;
10538             duration = o.duration || 1;
10539             this.show();
10540
10541             var b = this.getBox();
10542             var animFn = function(){
10543                 var proxy = this.createProxy({
10544
10545                      style:{
10546                         visbility:"hidden",
10547                         position:"absolute",
10548                         "z-index":"35000", // yee haw
10549                         border:"0px solid " + color
10550                      }
10551                   });
10552                 var scale = Roo.isBorderBox ? 2 : 1;
10553                 proxy.animate({
10554                     top:{from:b.y, to:b.y - 20},
10555                     left:{from:b.x, to:b.x - 20},
10556                     borderWidth:{from:0, to:10},
10557                     opacity:{from:1, to:0},
10558                     height:{from:b.height, to:(b.height + (20*scale))},
10559                     width:{from:b.width, to:(b.width + (20*scale))}
10560                 }, duration, function(){
10561                     proxy.remove();
10562                 });
10563                 if(--count > 0){
10564                      animFn.defer((duration/2)*1000, this);
10565                 }else{
10566                     el.afterFx(o);
10567                 }
10568             };
10569             animFn.call(this);
10570         });
10571         return this;
10572     },
10573
10574    /**
10575     * Creates a pause before any subsequent queued effects begin.  If there are
10576     * no effects queued after the pause it will have no effect.
10577     * Usage:
10578 <pre><code>
10579 el.pause(1);
10580 </code></pre>
10581     * @param {Number} seconds The length of time to pause (in seconds)
10582     * @return {Roo.Element} The Element
10583     */
10584     pause : function(seconds){
10585         var el = this.getFxEl();
10586         var o = {};
10587
10588         el.queueFx(o, function(){
10589             setTimeout(function(){
10590                 el.afterFx(o);
10591             }, seconds * 1000);
10592         });
10593         return this;
10594     },
10595
10596    /**
10597     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10598     * using the "endOpacity" config option.
10599     * Usage:
10600 <pre><code>
10601 // default: fade in from opacity 0 to 100%
10602 el.fadeIn();
10603
10604 // custom: fade in from opacity 0 to 75% over 2 seconds
10605 el.fadeIn({ endOpacity: .75, duration: 2});
10606
10607 // common config options shown with default values
10608 el.fadeIn({
10609     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10610     easing: 'easeOut',
10611     duration: .5
10612 });
10613 </code></pre>
10614     * @param {Object} options (optional) Object literal with any of the Fx config options
10615     * @return {Roo.Element} The Element
10616     */
10617     fadeIn : function(o){
10618         var el = this.getFxEl();
10619         o = o || {};
10620         el.queueFx(o, function(){
10621             this.setOpacity(0);
10622             this.fixDisplay();
10623             this.dom.style.visibility = 'visible';
10624             var to = o.endOpacity || 1;
10625             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10626                 o, null, .5, "easeOut", function(){
10627                 if(to == 1){
10628                     this.clearOpacity();
10629                 }
10630                 el.afterFx(o);
10631             });
10632         });
10633         return this;
10634     },
10635
10636    /**
10637     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10638     * using the "endOpacity" config option.
10639     * Usage:
10640 <pre><code>
10641 // default: fade out from the element's current opacity to 0
10642 el.fadeOut();
10643
10644 // custom: fade out from the element's current opacity to 25% over 2 seconds
10645 el.fadeOut({ endOpacity: .25, duration: 2});
10646
10647 // common config options shown with default values
10648 el.fadeOut({
10649     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10650     easing: 'easeOut',
10651     duration: .5
10652     remove: false,
10653     useDisplay: false
10654 });
10655 </code></pre>
10656     * @param {Object} options (optional) Object literal with any of the Fx config options
10657     * @return {Roo.Element} The Element
10658     */
10659     fadeOut : function(o){
10660         var el = this.getFxEl();
10661         o = o || {};
10662         el.queueFx(o, function(){
10663             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10664                 o, null, .5, "easeOut", function(){
10665                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10666                      this.dom.style.display = "none";
10667                 }else{
10668                      this.dom.style.visibility = "hidden";
10669                 }
10670                 this.clearOpacity();
10671                 el.afterFx(o);
10672             });
10673         });
10674         return this;
10675     },
10676
10677    /**
10678     * Animates the transition of an element's dimensions from a starting height/width
10679     * to an ending height/width.
10680     * Usage:
10681 <pre><code>
10682 // change height and width to 100x100 pixels
10683 el.scale(100, 100);
10684
10685 // common config options shown with default values.  The height and width will default to
10686 // the element's existing values if passed as null.
10687 el.scale(
10688     [element's width],
10689     [element's height], {
10690     easing: 'easeOut',
10691     duration: .35
10692 });
10693 </code></pre>
10694     * @param {Number} width  The new width (pass undefined to keep the original width)
10695     * @param {Number} height  The new height (pass undefined to keep the original height)
10696     * @param {Object} options (optional) Object literal with any of the Fx config options
10697     * @return {Roo.Element} The Element
10698     */
10699     scale : function(w, h, o){
10700         this.shift(Roo.apply({}, o, {
10701             width: w,
10702             height: h
10703         }));
10704         return this;
10705     },
10706
10707    /**
10708     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10709     * Any of these properties not specified in the config object will not be changed.  This effect 
10710     * requires that at least one new dimension, position or opacity setting must be passed in on
10711     * the config object in order for the function to have any effect.
10712     * Usage:
10713 <pre><code>
10714 // slide the element horizontally to x position 200 while changing the height and opacity
10715 el.shift({ x: 200, height: 50, opacity: .8 });
10716
10717 // common config options shown with default values.
10718 el.shift({
10719     width: [element's width],
10720     height: [element's height],
10721     x: [element's x position],
10722     y: [element's y position],
10723     opacity: [element's opacity],
10724     easing: 'easeOut',
10725     duration: .35
10726 });
10727 </code></pre>
10728     * @param {Object} options  Object literal with any of the Fx config options
10729     * @return {Roo.Element} The Element
10730     */
10731     shift : function(o){
10732         var el = this.getFxEl();
10733         o = o || {};
10734         el.queueFx(o, function(){
10735             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10736             if(w !== undefined){
10737                 a.width = {to: this.adjustWidth(w)};
10738             }
10739             if(h !== undefined){
10740                 a.height = {to: this.adjustHeight(h)};
10741             }
10742             if(x !== undefined || y !== undefined){
10743                 a.points = {to: [
10744                     x !== undefined ? x : this.getX(),
10745                     y !== undefined ? y : this.getY()
10746                 ]};
10747             }
10748             if(op !== undefined){
10749                 a.opacity = {to: op};
10750             }
10751             if(o.xy !== undefined){
10752                 a.points = {to: o.xy};
10753             }
10754             arguments.callee.anim = this.fxanim(a,
10755                 o, 'motion', .35, "easeOut", function(){
10756                 el.afterFx(o);
10757             });
10758         });
10759         return this;
10760     },
10761
10762         /**
10763          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10764          * ending point of the effect.
10765          * Usage:
10766          *<pre><code>
10767 // default: slide the element downward while fading out
10768 el.ghost();
10769
10770 // custom: slide the element out to the right with a 2-second duration
10771 el.ghost('r', { duration: 2 });
10772
10773 // common config options shown with default values
10774 el.ghost('b', {
10775     easing: 'easeOut',
10776     duration: .5
10777     remove: false,
10778     useDisplay: false
10779 });
10780 </code></pre>
10781          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10782          * @param {Object} options (optional) Object literal with any of the Fx config options
10783          * @return {Roo.Element} The Element
10784          */
10785     ghost : function(anchor, o){
10786         var el = this.getFxEl();
10787         o = o || {};
10788
10789         el.queueFx(o, function(){
10790             anchor = anchor || "b";
10791
10792             // restore values after effect
10793             var r = this.getFxRestore();
10794             var w = this.getWidth(),
10795                 h = this.getHeight();
10796
10797             var st = this.dom.style;
10798
10799             var after = function(){
10800                 if(o.useDisplay){
10801                     el.setDisplayed(false);
10802                 }else{
10803                     el.hide();
10804                 }
10805
10806                 el.clearOpacity();
10807                 el.setPositioning(r.pos);
10808                 st.width = r.width;
10809                 st.height = r.height;
10810
10811                 el.afterFx(o);
10812             };
10813
10814             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10815             switch(anchor.toLowerCase()){
10816                 case "t":
10817                     pt.by = [0, -h];
10818                 break;
10819                 case "l":
10820                     pt.by = [-w, 0];
10821                 break;
10822                 case "r":
10823                     pt.by = [w, 0];
10824                 break;
10825                 case "b":
10826                     pt.by = [0, h];
10827                 break;
10828                 case "tl":
10829                     pt.by = [-w, -h];
10830                 break;
10831                 case "bl":
10832                     pt.by = [-w, h];
10833                 break;
10834                 case "br":
10835                     pt.by = [w, h];
10836                 break;
10837                 case "tr":
10838                     pt.by = [w, -h];
10839                 break;
10840             }
10841
10842             arguments.callee.anim = this.fxanim(a,
10843                 o,
10844                 'motion',
10845                 .5,
10846                 "easeOut", after);
10847         });
10848         return this;
10849     },
10850
10851         /**
10852          * Ensures that all effects queued after syncFx is called on the element are
10853          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10854          * @return {Roo.Element} The Element
10855          */
10856     syncFx : function(){
10857         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10858             block : false,
10859             concurrent : true,
10860             stopFx : false
10861         });
10862         return this;
10863     },
10864
10865         /**
10866          * Ensures that all effects queued after sequenceFx is called on the element are
10867          * run in sequence.  This is the opposite of {@link #syncFx}.
10868          * @return {Roo.Element} The Element
10869          */
10870     sequenceFx : function(){
10871         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10872             block : false,
10873             concurrent : false,
10874             stopFx : false
10875         });
10876         return this;
10877     },
10878
10879         /* @private */
10880     nextFx : function(){
10881         var ef = this.fxQueue[0];
10882         if(ef){
10883             ef.call(this);
10884         }
10885     },
10886
10887         /**
10888          * Returns true if the element has any effects actively running or queued, else returns false.
10889          * @return {Boolean} True if element has active effects, else false
10890          */
10891     hasActiveFx : function(){
10892         return this.fxQueue && this.fxQueue[0];
10893     },
10894
10895         /**
10896          * Stops any running effects and clears the element's internal effects queue if it contains
10897          * any additional effects that haven't started yet.
10898          * @return {Roo.Element} The Element
10899          */
10900     stopFx : function(){
10901         if(this.hasActiveFx()){
10902             var cur = this.fxQueue[0];
10903             if(cur && cur.anim && cur.anim.isAnimated()){
10904                 this.fxQueue = [cur]; // clear out others
10905                 cur.anim.stop(true);
10906             }
10907         }
10908         return this;
10909     },
10910
10911         /* @private */
10912     beforeFx : function(o){
10913         if(this.hasActiveFx() && !o.concurrent){
10914            if(o.stopFx){
10915                this.stopFx();
10916                return true;
10917            }
10918            return false;
10919         }
10920         return true;
10921     },
10922
10923         /**
10924          * Returns true if the element is currently blocking so that no other effect can be queued
10925          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10926          * used to ensure that an effect initiated by a user action runs to completion prior to the
10927          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10928          * @return {Boolean} True if blocking, else false
10929          */
10930     hasFxBlock : function(){
10931         var q = this.fxQueue;
10932         return q && q[0] && q[0].block;
10933     },
10934
10935         /* @private */
10936     queueFx : function(o, fn){
10937         if(!this.fxQueue){
10938             this.fxQueue = [];
10939         }
10940         if(!this.hasFxBlock()){
10941             Roo.applyIf(o, this.fxDefaults);
10942             if(!o.concurrent){
10943                 var run = this.beforeFx(o);
10944                 fn.block = o.block;
10945                 this.fxQueue.push(fn);
10946                 if(run){
10947                     this.nextFx();
10948                 }
10949             }else{
10950                 fn.call(this);
10951             }
10952         }
10953         return this;
10954     },
10955
10956         /* @private */
10957     fxWrap : function(pos, o, vis){
10958         var wrap;
10959         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10960             var wrapXY;
10961             if(o.fixPosition){
10962                 wrapXY = this.getXY();
10963             }
10964             var div = document.createElement("div");
10965             div.style.visibility = vis;
10966             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10967             wrap.setPositioning(pos);
10968             if(wrap.getStyle("position") == "static"){
10969                 wrap.position("relative");
10970             }
10971             this.clearPositioning('auto');
10972             wrap.clip();
10973             wrap.dom.appendChild(this.dom);
10974             if(wrapXY){
10975                 wrap.setXY(wrapXY);
10976             }
10977         }
10978         return wrap;
10979     },
10980
10981         /* @private */
10982     fxUnwrap : function(wrap, pos, o){
10983         this.clearPositioning();
10984         this.setPositioning(pos);
10985         if(!o.wrap){
10986             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10987             wrap.remove();
10988         }
10989     },
10990
10991         /* @private */
10992     getFxRestore : function(){
10993         var st = this.dom.style;
10994         return {pos: this.getPositioning(), width: st.width, height : st.height};
10995     },
10996
10997         /* @private */
10998     afterFx : function(o){
10999         if(o.afterStyle){
11000             this.applyStyles(o.afterStyle);
11001         }
11002         if(o.afterCls){
11003             this.addClass(o.afterCls);
11004         }
11005         if(o.remove === true){
11006             this.remove();
11007         }
11008         Roo.callback(o.callback, o.scope, [this]);
11009         if(!o.concurrent){
11010             this.fxQueue.shift();
11011             this.nextFx();
11012         }
11013     },
11014
11015         /* @private */
11016     getFxEl : function(){ // support for composite element fx
11017         return Roo.get(this.dom);
11018     },
11019
11020         /* @private */
11021     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11022         animType = animType || 'run';
11023         opt = opt || {};
11024         var anim = Roo.lib.Anim[animType](
11025             this.dom, args,
11026             (opt.duration || defaultDur) || .35,
11027             (opt.easing || defaultEase) || 'easeOut',
11028             function(){
11029                 Roo.callback(cb, this);
11030             },
11031             this
11032         );
11033         opt.anim = anim;
11034         return anim;
11035     }
11036 };
11037
11038 // backwords compat
11039 Roo.Fx.resize = Roo.Fx.scale;
11040
11041 //When included, Roo.Fx is automatically applied to Element so that all basic
11042 //effects are available directly via the Element API
11043 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11044  * Based on:
11045  * Ext JS Library 1.1.1
11046  * Copyright(c) 2006-2007, Ext JS, LLC.
11047  *
11048  * Originally Released Under LGPL - original licence link has changed is not relivant.
11049  *
11050  * Fork - LGPL
11051  * <script type="text/javascript">
11052  */
11053
11054
11055 /**
11056  * @class Roo.CompositeElement
11057  * Standard composite class. Creates a Roo.Element for every element in the collection.
11058  * <br><br>
11059  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11060  * actions will be performed on all the elements in this collection.</b>
11061  * <br><br>
11062  * All methods return <i>this</i> and can be chained.
11063  <pre><code>
11064  var els = Roo.select("#some-el div.some-class", true);
11065  // or select directly from an existing element
11066  var el = Roo.get('some-el');
11067  el.select('div.some-class', true);
11068
11069  els.setWidth(100); // all elements become 100 width
11070  els.hide(true); // all elements fade out and hide
11071  // or
11072  els.setWidth(100).hide(true);
11073  </code></pre>
11074  */
11075 Roo.CompositeElement = function(els){
11076     this.elements = [];
11077     this.addElements(els);
11078 };
11079 Roo.CompositeElement.prototype = {
11080     isComposite: true,
11081     addElements : function(els){
11082         if(!els) {
11083             return this;
11084         }
11085         if(typeof els == "string"){
11086             els = Roo.Element.selectorFunction(els);
11087         }
11088         var yels = this.elements;
11089         var index = yels.length-1;
11090         for(var i = 0, len = els.length; i < len; i++) {
11091                 yels[++index] = Roo.get(els[i]);
11092         }
11093         return this;
11094     },
11095
11096     /**
11097     * Clears this composite and adds the elements returned by the passed selector.
11098     * @param {String/Array} els A string CSS selector, an array of elements or an element
11099     * @return {CompositeElement} this
11100     */
11101     fill : function(els){
11102         this.elements = [];
11103         this.add(els);
11104         return this;
11105     },
11106
11107     /**
11108     * Filters this composite to only elements that match the passed selector.
11109     * @param {String} selector A string CSS selector
11110     * @param {Boolean} inverse return inverse filter (not matches)
11111     * @return {CompositeElement} this
11112     */
11113     filter : function(selector, inverse){
11114         var els = [];
11115         inverse = inverse || false;
11116         this.each(function(el){
11117             var match = inverse ? !el.is(selector) : el.is(selector);
11118             if(match){
11119                 els[els.length] = el.dom;
11120             }
11121         });
11122         this.fill(els);
11123         return this;
11124     },
11125
11126     invoke : function(fn, args){
11127         var els = this.elements;
11128         for(var i = 0, len = els.length; i < len; i++) {
11129                 Roo.Element.prototype[fn].apply(els[i], args);
11130         }
11131         return this;
11132     },
11133     /**
11134     * Adds elements to this composite.
11135     * @param {String/Array} els A string CSS selector, an array of elements or an element
11136     * @return {CompositeElement} this
11137     */
11138     add : function(els){
11139         if(typeof els == "string"){
11140             this.addElements(Roo.Element.selectorFunction(els));
11141         }else if(els.length !== undefined){
11142             this.addElements(els);
11143         }else{
11144             this.addElements([els]);
11145         }
11146         return this;
11147     },
11148     /**
11149     * Calls the passed function passing (el, this, index) for each element in this composite.
11150     * @param {Function} fn The function to call
11151     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11152     * @return {CompositeElement} this
11153     */
11154     each : function(fn, scope){
11155         var els = this.elements;
11156         for(var i = 0, len = els.length; i < len; i++){
11157             if(fn.call(scope || els[i], els[i], this, i) === false) {
11158                 break;
11159             }
11160         }
11161         return this;
11162     },
11163
11164     /**
11165      * Returns the Element object at the specified index
11166      * @param {Number} index
11167      * @return {Roo.Element}
11168      */
11169     item : function(index){
11170         return this.elements[index] || null;
11171     },
11172
11173     /**
11174      * Returns the first Element
11175      * @return {Roo.Element}
11176      */
11177     first : function(){
11178         return this.item(0);
11179     },
11180
11181     /**
11182      * Returns the last Element
11183      * @return {Roo.Element}
11184      */
11185     last : function(){
11186         return this.item(this.elements.length-1);
11187     },
11188
11189     /**
11190      * Returns the number of elements in this composite
11191      * @return Number
11192      */
11193     getCount : function(){
11194         return this.elements.length;
11195     },
11196
11197     /**
11198      * Returns true if this composite contains the passed element
11199      * @return Boolean
11200      */
11201     contains : function(el){
11202         return this.indexOf(el) !== -1;
11203     },
11204
11205     /**
11206      * Returns true if this composite contains the passed element
11207      * @return Boolean
11208      */
11209     indexOf : function(el){
11210         return this.elements.indexOf(Roo.get(el));
11211     },
11212
11213
11214     /**
11215     * Removes the specified element(s).
11216     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11217     * or an array of any of those.
11218     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11219     * @return {CompositeElement} this
11220     */
11221     removeElement : function(el, removeDom){
11222         if(el instanceof Array){
11223             for(var i = 0, len = el.length; i < len; i++){
11224                 this.removeElement(el[i]);
11225             }
11226             return this;
11227         }
11228         var index = typeof el == 'number' ? el : this.indexOf(el);
11229         if(index !== -1){
11230             if(removeDom){
11231                 var d = this.elements[index];
11232                 if(d.dom){
11233                     d.remove();
11234                 }else{
11235                     d.parentNode.removeChild(d);
11236                 }
11237             }
11238             this.elements.splice(index, 1);
11239         }
11240         return this;
11241     },
11242
11243     /**
11244     * Replaces the specified element with the passed element.
11245     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11246     * to replace.
11247     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11248     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11249     * @return {CompositeElement} this
11250     */
11251     replaceElement : function(el, replacement, domReplace){
11252         var index = typeof el == 'number' ? el : this.indexOf(el);
11253         if(index !== -1){
11254             if(domReplace){
11255                 this.elements[index].replaceWith(replacement);
11256             }else{
11257                 this.elements.splice(index, 1, Roo.get(replacement))
11258             }
11259         }
11260         return this;
11261     },
11262
11263     /**
11264      * Removes all elements.
11265      */
11266     clear : function(){
11267         this.elements = [];
11268     }
11269 };
11270 (function(){
11271     Roo.CompositeElement.createCall = function(proto, fnName){
11272         if(!proto[fnName]){
11273             proto[fnName] = function(){
11274                 return this.invoke(fnName, arguments);
11275             };
11276         }
11277     };
11278     for(var fnName in Roo.Element.prototype){
11279         if(typeof Roo.Element.prototype[fnName] == "function"){
11280             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11281         }
11282     };
11283 })();
11284 /*
11285  * Based on:
11286  * Ext JS Library 1.1.1
11287  * Copyright(c) 2006-2007, Ext JS, LLC.
11288  *
11289  * Originally Released Under LGPL - original licence link has changed is not relivant.
11290  *
11291  * Fork - LGPL
11292  * <script type="text/javascript">
11293  */
11294
11295 /**
11296  * @class Roo.CompositeElementLite
11297  * @extends Roo.CompositeElement
11298  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11299  <pre><code>
11300  var els = Roo.select("#some-el div.some-class");
11301  // or select directly from an existing element
11302  var el = Roo.get('some-el');
11303  el.select('div.some-class');
11304
11305  els.setWidth(100); // all elements become 100 width
11306  els.hide(true); // all elements fade out and hide
11307  // or
11308  els.setWidth(100).hide(true);
11309  </code></pre><br><br>
11310  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11311  * actions will be performed on all the elements in this collection.</b>
11312  */
11313 Roo.CompositeElementLite = function(els){
11314     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11315     this.el = new Roo.Element.Flyweight();
11316 };
11317 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11318     addElements : function(els){
11319         if(els){
11320             if(els instanceof Array){
11321                 this.elements = this.elements.concat(els);
11322             }else{
11323                 var yels = this.elements;
11324                 var index = yels.length-1;
11325                 for(var i = 0, len = els.length; i < len; i++) {
11326                     yels[++index] = els[i];
11327                 }
11328             }
11329         }
11330         return this;
11331     },
11332     invoke : function(fn, args){
11333         var els = this.elements;
11334         var el = this.el;
11335         for(var i = 0, len = els.length; i < len; i++) {
11336             el.dom = els[i];
11337                 Roo.Element.prototype[fn].apply(el, args);
11338         }
11339         return this;
11340     },
11341     /**
11342      * Returns a flyweight Element of the dom element object at the specified index
11343      * @param {Number} index
11344      * @return {Roo.Element}
11345      */
11346     item : function(index){
11347         if(!this.elements[index]){
11348             return null;
11349         }
11350         this.el.dom = this.elements[index];
11351         return this.el;
11352     },
11353
11354     // fixes scope with flyweight
11355     addListener : function(eventName, handler, scope, opt){
11356         var els = this.elements;
11357         for(var i = 0, len = els.length; i < len; i++) {
11358             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11359         }
11360         return this;
11361     },
11362
11363     /**
11364     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11365     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11366     * a reference to the dom node, use el.dom.</b>
11367     * @param {Function} fn The function to call
11368     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11369     * @return {CompositeElement} this
11370     */
11371     each : function(fn, scope){
11372         var els = this.elements;
11373         var el = this.el;
11374         for(var i = 0, len = els.length; i < len; i++){
11375             el.dom = els[i];
11376                 if(fn.call(scope || el, el, this, i) === false){
11377                 break;
11378             }
11379         }
11380         return this;
11381     },
11382
11383     indexOf : function(el){
11384         return this.elements.indexOf(Roo.getDom(el));
11385     },
11386
11387     replaceElement : function(el, replacement, domReplace){
11388         var index = typeof el == 'number' ? el : this.indexOf(el);
11389         if(index !== -1){
11390             replacement = Roo.getDom(replacement);
11391             if(domReplace){
11392                 var d = this.elements[index];
11393                 d.parentNode.insertBefore(replacement, d);
11394                 d.parentNode.removeChild(d);
11395             }
11396             this.elements.splice(index, 1, replacement);
11397         }
11398         return this;
11399     }
11400 });
11401 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11402
11403 /*
11404  * Based on:
11405  * Ext JS Library 1.1.1
11406  * Copyright(c) 2006-2007, Ext JS, LLC.
11407  *
11408  * Originally Released Under LGPL - original licence link has changed is not relivant.
11409  *
11410  * Fork - LGPL
11411  * <script type="text/javascript">
11412  */
11413
11414  
11415
11416 /**
11417  * @class Roo.data.Connection
11418  * @extends Roo.util.Observable
11419  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11420  * either to a configured URL, or to a URL specified at request time.<br><br>
11421  * <p>
11422  * Requests made by this class are asynchronous, and will return immediately. No data from
11423  * the server will be available to the statement immediately following the {@link #request} call.
11424  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11425  * <p>
11426  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11427  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11428  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11429  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11430  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11431  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11432  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11433  * standard DOM methods.
11434  * @constructor
11435  * @param {Object} config a configuration object.
11436  */
11437 Roo.data.Connection = function(config){
11438     Roo.apply(this, config);
11439     this.addEvents({
11440         /**
11441          * @event beforerequest
11442          * Fires before a network request is made to retrieve a data object.
11443          * @param {Connection} conn This Connection object.
11444          * @param {Object} options The options config object passed to the {@link #request} method.
11445          */
11446         "beforerequest" : true,
11447         /**
11448          * @event requestcomplete
11449          * Fires if the request was successfully completed.
11450          * @param {Connection} conn This Connection object.
11451          * @param {Object} response The XHR object containing the response data.
11452          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11453          * @param {Object} options The options config object passed to the {@link #request} method.
11454          */
11455         "requestcomplete" : true,
11456         /**
11457          * @event requestexception
11458          * Fires if an error HTTP status was returned from the server.
11459          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11460          * @param {Connection} conn This Connection object.
11461          * @param {Object} response The XHR object containing the response data.
11462          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11463          * @param {Object} options The options config object passed to the {@link #request} method.
11464          */
11465         "requestexception" : true
11466     });
11467     Roo.data.Connection.superclass.constructor.call(this);
11468 };
11469
11470 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11471     /**
11472      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11473      */
11474     /**
11475      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11476      * extra parameters to each request made by this object. (defaults to undefined)
11477      */
11478     /**
11479      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11480      *  to each request made by this object. (defaults to undefined)
11481      */
11482     /**
11483      * @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)
11484      */
11485     /**
11486      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11487      */
11488     timeout : 30000,
11489     /**
11490      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11491      * @type Boolean
11492      */
11493     autoAbort:false,
11494
11495     /**
11496      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11497      * @type Boolean
11498      */
11499     disableCaching: true,
11500
11501     /**
11502      * Sends an HTTP request to a remote server.
11503      * @param {Object} options An object which may contain the following properties:<ul>
11504      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11505      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11506      * request, a url encoded string or a function to call to get either.</li>
11507      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11508      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11509      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11510      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11511      * <li>options {Object} The parameter to the request call.</li>
11512      * <li>success {Boolean} True if the request succeeded.</li>
11513      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11514      * </ul></li>
11515      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11516      * The callback is passed the following parameters:<ul>
11517      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11518      * <li>options {Object} The parameter to the request call.</li>
11519      * </ul></li>
11520      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11521      * The callback is passed the following parameters:<ul>
11522      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11523      * <li>options {Object} The parameter to the request call.</li>
11524      * </ul></li>
11525      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11526      * for the callback function. Defaults to the browser window.</li>
11527      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11528      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11529      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11530      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11531      * params for the post data. Any params will be appended to the URL.</li>
11532      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11533      * </ul>
11534      * @return {Number} transactionId
11535      */
11536     request : function(o){
11537         if(this.fireEvent("beforerequest", this, o) !== false){
11538             var p = o.params;
11539
11540             if(typeof p == "function"){
11541                 p = p.call(o.scope||window, o);
11542             }
11543             if(typeof p == "object"){
11544                 p = Roo.urlEncode(o.params);
11545             }
11546             if(this.extraParams){
11547                 var extras = Roo.urlEncode(this.extraParams);
11548                 p = p ? (p + '&' + extras) : extras;
11549             }
11550
11551             var url = o.url || this.url;
11552             if(typeof url == 'function'){
11553                 url = url.call(o.scope||window, o);
11554             }
11555
11556             if(o.form){
11557                 var form = Roo.getDom(o.form);
11558                 url = url || form.action;
11559
11560                 var enctype = form.getAttribute("enctype");
11561                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11562                     return this.doFormUpload(o, p, url);
11563                 }
11564                 var f = Roo.lib.Ajax.serializeForm(form);
11565                 p = p ? (p + '&' + f) : f;
11566             }
11567
11568             var hs = o.headers;
11569             if(this.defaultHeaders){
11570                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11571                 if(!o.headers){
11572                     o.headers = hs;
11573                 }
11574             }
11575
11576             var cb = {
11577                 success: this.handleResponse,
11578                 failure: this.handleFailure,
11579                 scope: this,
11580                 argument: {options: o},
11581                 timeout : o.timeout || this.timeout
11582             };
11583
11584             var method = o.method||this.method||(p ? "POST" : "GET");
11585
11586             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11587                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11588             }
11589
11590             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11591                 if(o.autoAbort){
11592                     this.abort();
11593                 }
11594             }else if(this.autoAbort !== false){
11595                 this.abort();
11596             }
11597
11598             if((method == 'GET' && p) || o.xmlData){
11599                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11600                 p = '';
11601             }
11602             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11603             return this.transId;
11604         }else{
11605             Roo.callback(o.callback, o.scope, [o, null, null]);
11606             return null;
11607         }
11608     },
11609
11610     /**
11611      * Determine whether this object has a request outstanding.
11612      * @param {Number} transactionId (Optional) defaults to the last transaction
11613      * @return {Boolean} True if there is an outstanding request.
11614      */
11615     isLoading : function(transId){
11616         if(transId){
11617             return Roo.lib.Ajax.isCallInProgress(transId);
11618         }else{
11619             return this.transId ? true : false;
11620         }
11621     },
11622
11623     /**
11624      * Aborts any outstanding request.
11625      * @param {Number} transactionId (Optional) defaults to the last transaction
11626      */
11627     abort : function(transId){
11628         if(transId || this.isLoading()){
11629             Roo.lib.Ajax.abort(transId || this.transId);
11630         }
11631     },
11632
11633     // private
11634     handleResponse : function(response){
11635         this.transId = false;
11636         var options = response.argument.options;
11637         response.argument = options ? options.argument : null;
11638         this.fireEvent("requestcomplete", this, response, options);
11639         Roo.callback(options.success, options.scope, [response, options]);
11640         Roo.callback(options.callback, options.scope, [options, true, response]);
11641     },
11642
11643     // private
11644     handleFailure : function(response, e){
11645         this.transId = false;
11646         var options = response.argument.options;
11647         response.argument = options ? options.argument : null;
11648         this.fireEvent("requestexception", this, response, options, e);
11649         Roo.callback(options.failure, options.scope, [response, options]);
11650         Roo.callback(options.callback, options.scope, [options, false, response]);
11651     },
11652
11653     // private
11654     doFormUpload : function(o, ps, url){
11655         var id = Roo.id();
11656         var frame = document.createElement('iframe');
11657         frame.id = id;
11658         frame.name = id;
11659         frame.className = 'x-hidden';
11660         if(Roo.isIE){
11661             frame.src = Roo.SSL_SECURE_URL;
11662         }
11663         document.body.appendChild(frame);
11664
11665         if(Roo.isIE){
11666            document.frames[id].name = id;
11667         }
11668
11669         var form = Roo.getDom(o.form);
11670         form.target = id;
11671         form.method = 'POST';
11672         form.enctype = form.encoding = 'multipart/form-data';
11673         if(url){
11674             form.action = url;
11675         }
11676
11677         var hiddens, hd;
11678         if(ps){ // add dynamic params
11679             hiddens = [];
11680             ps = Roo.urlDecode(ps, false);
11681             for(var k in ps){
11682                 if(ps.hasOwnProperty(k)){
11683                     hd = document.createElement('input');
11684                     hd.type = 'hidden';
11685                     hd.name = k;
11686                     hd.value = ps[k];
11687                     form.appendChild(hd);
11688                     hiddens.push(hd);
11689                 }
11690             }
11691         }
11692
11693         function cb(){
11694             var r = {  // bogus response object
11695                 responseText : '',
11696                 responseXML : null
11697             };
11698
11699             r.argument = o ? o.argument : null;
11700
11701             try { //
11702                 var doc;
11703                 if(Roo.isIE){
11704                     doc = frame.contentWindow.document;
11705                 }else {
11706                     doc = (frame.contentDocument || window.frames[id].document);
11707                 }
11708                 if(doc && doc.body){
11709                     r.responseText = doc.body.innerHTML;
11710                 }
11711                 if(doc && doc.XMLDocument){
11712                     r.responseXML = doc.XMLDocument;
11713                 }else {
11714                     r.responseXML = doc;
11715                 }
11716             }
11717             catch(e) {
11718                 // ignore
11719             }
11720
11721             Roo.EventManager.removeListener(frame, 'load', cb, this);
11722
11723             this.fireEvent("requestcomplete", this, r, o);
11724             Roo.callback(o.success, o.scope, [r, o]);
11725             Roo.callback(o.callback, o.scope, [o, true, r]);
11726
11727             setTimeout(function(){document.body.removeChild(frame);}, 100);
11728         }
11729
11730         Roo.EventManager.on(frame, 'load', cb, this);
11731         form.submit();
11732
11733         if(hiddens){ // remove dynamic params
11734             for(var i = 0, len = hiddens.length; i < len; i++){
11735                 form.removeChild(hiddens[i]);
11736             }
11737         }
11738     }
11739 });
11740 /*
11741  * Based on:
11742  * Ext JS Library 1.1.1
11743  * Copyright(c) 2006-2007, Ext JS, LLC.
11744  *
11745  * Originally Released Under LGPL - original licence link has changed is not relivant.
11746  *
11747  * Fork - LGPL
11748  * <script type="text/javascript">
11749  */
11750  
11751 /**
11752  * Global Ajax request class.
11753  * 
11754  * @class Roo.Ajax
11755  * @extends Roo.data.Connection
11756  * @static
11757  * 
11758  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11759  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11760  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11761  * @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)
11762  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11763  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11764  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11765  */
11766 Roo.Ajax = new Roo.data.Connection({
11767     // fix up the docs
11768     /**
11769      * @scope Roo.Ajax
11770      * @type {Boolear} 
11771      */
11772     autoAbort : false,
11773
11774     /**
11775      * Serialize the passed form into a url encoded string
11776      * @scope Roo.Ajax
11777      * @param {String/HTMLElement} form
11778      * @return {String}
11779      */
11780     serializeForm : function(form){
11781         return Roo.lib.Ajax.serializeForm(form);
11782     }
11783 });/*
11784  * Based on:
11785  * Ext JS Library 1.1.1
11786  * Copyright(c) 2006-2007, Ext JS, LLC.
11787  *
11788  * Originally Released Under LGPL - original licence link has changed is not relivant.
11789  *
11790  * Fork - LGPL
11791  * <script type="text/javascript">
11792  */
11793
11794  
11795 /**
11796  * @class Roo.UpdateManager
11797  * @extends Roo.util.Observable
11798  * Provides AJAX-style update for Element object.<br><br>
11799  * Usage:<br>
11800  * <pre><code>
11801  * // Get it from a Roo.Element object
11802  * var el = Roo.get("foo");
11803  * var mgr = el.getUpdateManager();
11804  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11805  * ...
11806  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11807  * <br>
11808  * // or directly (returns the same UpdateManager instance)
11809  * var mgr = new Roo.UpdateManager("myElementId");
11810  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11811  * mgr.on("update", myFcnNeedsToKnow);
11812  * <br>
11813    // short handed call directly from the element object
11814    Roo.get("foo").load({
11815         url: "bar.php",
11816         scripts:true,
11817         params: "for=bar",
11818         text: "Loading Foo..."
11819    });
11820  * </code></pre>
11821  * @constructor
11822  * Create new UpdateManager directly.
11823  * @param {String/HTMLElement/Roo.Element} el The element to update
11824  * @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).
11825  */
11826 Roo.UpdateManager = function(el, forceNew){
11827     el = Roo.get(el);
11828     if(!forceNew && el.updateManager){
11829         return el.updateManager;
11830     }
11831     /**
11832      * The Element object
11833      * @type Roo.Element
11834      */
11835     this.el = el;
11836     /**
11837      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11838      * @type String
11839      */
11840     this.defaultUrl = null;
11841
11842     this.addEvents({
11843         /**
11844          * @event beforeupdate
11845          * Fired before an update is made, return false from your handler and the update is cancelled.
11846          * @param {Roo.Element} el
11847          * @param {String/Object/Function} url
11848          * @param {String/Object} params
11849          */
11850         "beforeupdate": true,
11851         /**
11852          * @event update
11853          * Fired after successful update is made.
11854          * @param {Roo.Element} el
11855          * @param {Object} oResponseObject The response Object
11856          */
11857         "update": true,
11858         /**
11859          * @event failure
11860          * Fired on update failure.
11861          * @param {Roo.Element} el
11862          * @param {Object} oResponseObject The response Object
11863          */
11864         "failure": true
11865     });
11866     var d = Roo.UpdateManager.defaults;
11867     /**
11868      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11869      * @type String
11870      */
11871     this.sslBlankUrl = d.sslBlankUrl;
11872     /**
11873      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11874      * @type Boolean
11875      */
11876     this.disableCaching = d.disableCaching;
11877     /**
11878      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11879      * @type String
11880      */
11881     this.indicatorText = d.indicatorText;
11882     /**
11883      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11884      * @type String
11885      */
11886     this.showLoadIndicator = d.showLoadIndicator;
11887     /**
11888      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11889      * @type Number
11890      */
11891     this.timeout = d.timeout;
11892
11893     /**
11894      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11895      * @type Boolean
11896      */
11897     this.loadScripts = d.loadScripts;
11898
11899     /**
11900      * Transaction object of current executing transaction
11901      */
11902     this.transaction = null;
11903
11904     /**
11905      * @private
11906      */
11907     this.autoRefreshProcId = null;
11908     /**
11909      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11910      * @type Function
11911      */
11912     this.refreshDelegate = this.refresh.createDelegate(this);
11913     /**
11914      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11915      * @type Function
11916      */
11917     this.updateDelegate = this.update.createDelegate(this);
11918     /**
11919      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11920      * @type Function
11921      */
11922     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11923     /**
11924      * @private
11925      */
11926     this.successDelegate = this.processSuccess.createDelegate(this);
11927     /**
11928      * @private
11929      */
11930     this.failureDelegate = this.processFailure.createDelegate(this);
11931
11932     if(!this.renderer){
11933      /**
11934       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11935       */
11936     this.renderer = new Roo.UpdateManager.BasicRenderer();
11937     }
11938     
11939     Roo.UpdateManager.superclass.constructor.call(this);
11940 };
11941
11942 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11943     /**
11944      * Get the Element this UpdateManager is bound to
11945      * @return {Roo.Element} The element
11946      */
11947     getEl : function(){
11948         return this.el;
11949     },
11950     /**
11951      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11952      * @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:
11953 <pre><code>
11954 um.update({<br/>
11955     url: "your-url.php",<br/>
11956     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11957     callback: yourFunction,<br/>
11958     scope: yourObject, //(optional scope)  <br/>
11959     discardUrl: false, <br/>
11960     nocache: false,<br/>
11961     text: "Loading...",<br/>
11962     timeout: 30,<br/>
11963     scripts: false<br/>
11964 });
11965 </code></pre>
11966      * The only required property is url. The optional properties nocache, text and scripts
11967      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11968      * @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}
11969      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11970      * @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.
11971      */
11972     update : function(url, params, callback, discardUrl){
11973         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11974             var method = this.method,
11975                 cfg;
11976             if(typeof url == "object"){ // must be config object
11977                 cfg = url;
11978                 url = cfg.url;
11979                 params = params || cfg.params;
11980                 callback = callback || cfg.callback;
11981                 discardUrl = discardUrl || cfg.discardUrl;
11982                 if(callback && cfg.scope){
11983                     callback = callback.createDelegate(cfg.scope);
11984                 }
11985                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11986                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11987                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11988                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11989                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11990             }
11991             this.showLoading();
11992             if(!discardUrl){
11993                 this.defaultUrl = url;
11994             }
11995             if(typeof url == "function"){
11996                 url = url.call(this);
11997             }
11998
11999             method = method || (params ? "POST" : "GET");
12000             if(method == "GET"){
12001                 url = this.prepareUrl(url);
12002             }
12003
12004             var o = Roo.apply(cfg ||{}, {
12005                 url : url,
12006                 params: params,
12007                 success: this.successDelegate,
12008                 failure: this.failureDelegate,
12009                 callback: undefined,
12010                 timeout: (this.timeout*1000),
12011                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12012             });
12013             Roo.log("updated manager called with timeout of " + o.timeout);
12014             this.transaction = Roo.Ajax.request(o);
12015         }
12016     },
12017
12018     /**
12019      * 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.
12020      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12021      * @param {String/HTMLElement} form The form Id or form element
12022      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12023      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12024      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12025      */
12026     formUpdate : function(form, url, reset, callback){
12027         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12028             if(typeof url == "function"){
12029                 url = url.call(this);
12030             }
12031             form = Roo.getDom(form);
12032             this.transaction = Roo.Ajax.request({
12033                 form: form,
12034                 url:url,
12035                 success: this.successDelegate,
12036                 failure: this.failureDelegate,
12037                 timeout: (this.timeout*1000),
12038                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12039             });
12040             this.showLoading.defer(1, this);
12041         }
12042     },
12043
12044     /**
12045      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12046      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12047      */
12048     refresh : function(callback){
12049         if(this.defaultUrl == null){
12050             return;
12051         }
12052         this.update(this.defaultUrl, null, callback, true);
12053     },
12054
12055     /**
12056      * Set this element to auto refresh.
12057      * @param {Number} interval How often to update (in seconds).
12058      * @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)
12059      * @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}
12060      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12061      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12062      */
12063     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12064         if(refreshNow){
12065             this.update(url || this.defaultUrl, params, callback, true);
12066         }
12067         if(this.autoRefreshProcId){
12068             clearInterval(this.autoRefreshProcId);
12069         }
12070         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12071     },
12072
12073     /**
12074      * Stop auto refresh on this element.
12075      */
12076      stopAutoRefresh : function(){
12077         if(this.autoRefreshProcId){
12078             clearInterval(this.autoRefreshProcId);
12079             delete this.autoRefreshProcId;
12080         }
12081     },
12082
12083     isAutoRefreshing : function(){
12084        return this.autoRefreshProcId ? true : false;
12085     },
12086     /**
12087      * Called to update the element to "Loading" state. Override to perform custom action.
12088      */
12089     showLoading : function(){
12090         if(this.showLoadIndicator){
12091             this.el.update(this.indicatorText);
12092         }
12093     },
12094
12095     /**
12096      * Adds unique parameter to query string if disableCaching = true
12097      * @private
12098      */
12099     prepareUrl : function(url){
12100         if(this.disableCaching){
12101             var append = "_dc=" + (new Date().getTime());
12102             if(url.indexOf("?") !== -1){
12103                 url += "&" + append;
12104             }else{
12105                 url += "?" + append;
12106             }
12107         }
12108         return url;
12109     },
12110
12111     /**
12112      * @private
12113      */
12114     processSuccess : function(response){
12115         this.transaction = null;
12116         if(response.argument.form && response.argument.reset){
12117             try{ // put in try/catch since some older FF releases had problems with this
12118                 response.argument.form.reset();
12119             }catch(e){}
12120         }
12121         if(this.loadScripts){
12122             this.renderer.render(this.el, response, this,
12123                 this.updateComplete.createDelegate(this, [response]));
12124         }else{
12125             this.renderer.render(this.el, response, this);
12126             this.updateComplete(response);
12127         }
12128     },
12129
12130     updateComplete : function(response){
12131         this.fireEvent("update", this.el, response);
12132         if(typeof response.argument.callback == "function"){
12133             response.argument.callback(this.el, true, response);
12134         }
12135     },
12136
12137     /**
12138      * @private
12139      */
12140     processFailure : function(response){
12141         this.transaction = null;
12142         this.fireEvent("failure", this.el, response);
12143         if(typeof response.argument.callback == "function"){
12144             response.argument.callback(this.el, false, response);
12145         }
12146     },
12147
12148     /**
12149      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12150      * @param {Object} renderer The object implementing the render() method
12151      */
12152     setRenderer : function(renderer){
12153         this.renderer = renderer;
12154     },
12155
12156     getRenderer : function(){
12157        return this.renderer;
12158     },
12159
12160     /**
12161      * Set the defaultUrl used for updates
12162      * @param {String/Function} defaultUrl The url or a function to call to get the url
12163      */
12164     setDefaultUrl : function(defaultUrl){
12165         this.defaultUrl = defaultUrl;
12166     },
12167
12168     /**
12169      * Aborts the executing transaction
12170      */
12171     abort : function(){
12172         if(this.transaction){
12173             Roo.Ajax.abort(this.transaction);
12174         }
12175     },
12176
12177     /**
12178      * Returns true if an update is in progress
12179      * @return {Boolean}
12180      */
12181     isUpdating : function(){
12182         if(this.transaction){
12183             return Roo.Ajax.isLoading(this.transaction);
12184         }
12185         return false;
12186     }
12187 });
12188
12189 /**
12190  * @class Roo.UpdateManager.defaults
12191  * @static (not really - but it helps the doc tool)
12192  * The defaults collection enables customizing the default properties of UpdateManager
12193  */
12194    Roo.UpdateManager.defaults = {
12195        /**
12196          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12197          * @type Number
12198          */
12199          timeout : 30,
12200
12201          /**
12202          * True to process scripts by default (Defaults to false).
12203          * @type Boolean
12204          */
12205         loadScripts : false,
12206
12207         /**
12208         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12209         * @type String
12210         */
12211         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12212         /**
12213          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12214          * @type Boolean
12215          */
12216         disableCaching : false,
12217         /**
12218          * Whether to show indicatorText when loading (Defaults to true).
12219          * @type Boolean
12220          */
12221         showLoadIndicator : true,
12222         /**
12223          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12224          * @type String
12225          */
12226         indicatorText : '<div class="loading-indicator">Loading...</div>'
12227    };
12228
12229 /**
12230  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12231  *Usage:
12232  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12233  * @param {String/HTMLElement/Roo.Element} el The element to update
12234  * @param {String} url The url
12235  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12236  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12237  * @static
12238  * @deprecated
12239  * @member Roo.UpdateManager
12240  */
12241 Roo.UpdateManager.updateElement = function(el, url, params, options){
12242     var um = Roo.get(el, true).getUpdateManager();
12243     Roo.apply(um, options);
12244     um.update(url, params, options ? options.callback : null);
12245 };
12246 // alias for backwards compat
12247 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12248 /**
12249  * @class Roo.UpdateManager.BasicRenderer
12250  * Default Content renderer. Updates the elements innerHTML with the responseText.
12251  */
12252 Roo.UpdateManager.BasicRenderer = function(){};
12253
12254 Roo.UpdateManager.BasicRenderer.prototype = {
12255     /**
12256      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12257      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12258      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12259      * @param {Roo.Element} el The element being rendered
12260      * @param {Object} response The YUI Connect response object
12261      * @param {UpdateManager} updateManager The calling update manager
12262      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12263      */
12264      render : function(el, response, updateManager, callback){
12265         el.update(response.responseText, updateManager.loadScripts, callback);
12266     }
12267 };
12268 /*
12269  * Based on:
12270  * Roo JS
12271  * (c)) Alan Knowles
12272  * Licence : LGPL
12273  */
12274
12275
12276 /**
12277  * @class Roo.DomTemplate
12278  * @extends Roo.Template
12279  * An effort at a dom based template engine..
12280  *
12281  * Similar to XTemplate, except it uses dom parsing to create the template..
12282  *
12283  * Supported features:
12284  *
12285  *  Tags:
12286
12287 <pre><code>
12288       {a_variable} - output encoded.
12289       {a_variable.format:("Y-m-d")} - call a method on the variable
12290       {a_variable:raw} - unencoded output
12291       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12292       {a_variable:this.method_on_template(...)} - call a method on the template object.
12293  
12294 </code></pre>
12295  *  The tpl tag:
12296 <pre><code>
12297         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12298         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12299         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12300         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12301   
12302 </code></pre>
12303  *      
12304  */
12305 Roo.DomTemplate = function()
12306 {
12307      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12308      if (this.html) {
12309         this.compile();
12310      }
12311 };
12312
12313
12314 Roo.extend(Roo.DomTemplate, Roo.Template, {
12315     /**
12316      * id counter for sub templates.
12317      */
12318     id : 0,
12319     /**
12320      * flag to indicate if dom parser is inside a pre,
12321      * it will strip whitespace if not.
12322      */
12323     inPre : false,
12324     
12325     /**
12326      * The various sub templates
12327      */
12328     tpls : false,
12329     
12330     
12331     
12332     /**
12333      *
12334      * basic tag replacing syntax
12335      * WORD:WORD()
12336      *
12337      * // you can fake an object call by doing this
12338      *  x.t:(test,tesT) 
12339      * 
12340      */
12341     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12342     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12343     
12344     iterChild : function (node, method) {
12345         
12346         var oldPre = this.inPre;
12347         if (node.tagName == 'PRE') {
12348             this.inPre = true;
12349         }
12350         for( var i = 0; i < node.childNodes.length; i++) {
12351             method.call(this, node.childNodes[i]);
12352         }
12353         this.inPre = oldPre;
12354     },
12355     
12356     
12357     
12358     /**
12359      * compile the template
12360      *
12361      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12362      *
12363      */
12364     compile: function()
12365     {
12366         var s = this.html;
12367         
12368         // covert the html into DOM...
12369         var doc = false;
12370         var div =false;
12371         try {
12372             doc = document.implementation.createHTMLDocument("");
12373             doc.documentElement.innerHTML =   this.html  ;
12374             div = doc.documentElement;
12375         } catch (e) {
12376             // old IE... - nasty -- it causes all sorts of issues.. with
12377             // images getting pulled from server..
12378             div = document.createElement('div');
12379             div.innerHTML = this.html;
12380         }
12381         //doc.documentElement.innerHTML = htmlBody
12382          
12383         
12384         
12385         this.tpls = [];
12386         var _t = this;
12387         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12388         
12389         var tpls = this.tpls;
12390         
12391         // create a top level template from the snippet..
12392         
12393         //Roo.log(div.innerHTML);
12394         
12395         var tpl = {
12396             uid : 'master',
12397             id : this.id++,
12398             attr : false,
12399             value : false,
12400             body : div.innerHTML,
12401             
12402             forCall : false,
12403             execCall : false,
12404             dom : div,
12405             isTop : true
12406             
12407         };
12408         tpls.unshift(tpl);
12409         
12410         
12411         // compile them...
12412         this.tpls = [];
12413         Roo.each(tpls, function(tp){
12414             this.compileTpl(tp);
12415             this.tpls[tp.id] = tp;
12416         }, this);
12417         
12418         this.master = tpls[0];
12419         return this;
12420         
12421         
12422     },
12423     
12424     compileNode : function(node, istop) {
12425         // test for
12426         //Roo.log(node);
12427         
12428         
12429         // skip anything not a tag..
12430         if (node.nodeType != 1) {
12431             if (node.nodeType == 3 && !this.inPre) {
12432                 // reduce white space..
12433                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12434                 
12435             }
12436             return;
12437         }
12438         
12439         var tpl = {
12440             uid : false,
12441             id : false,
12442             attr : false,
12443             value : false,
12444             body : '',
12445             
12446             forCall : false,
12447             execCall : false,
12448             dom : false,
12449             isTop : istop
12450             
12451             
12452         };
12453         
12454         
12455         switch(true) {
12456             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12457             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12458             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12459             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12460             // no default..
12461         }
12462         
12463         
12464         if (!tpl.attr) {
12465             // just itterate children..
12466             this.iterChild(node,this.compileNode);
12467             return;
12468         }
12469         tpl.uid = this.id++;
12470         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12471         node.removeAttribute('roo-'+ tpl.attr);
12472         if (tpl.attr != 'name') {
12473             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12474             node.parentNode.replaceChild(placeholder,  node);
12475         } else {
12476             
12477             var placeholder =  document.createElement('span');
12478             placeholder.className = 'roo-tpl-' + tpl.value;
12479             node.parentNode.replaceChild(placeholder,  node);
12480         }
12481         
12482         // parent now sees '{domtplXXXX}
12483         this.iterChild(node,this.compileNode);
12484         
12485         // we should now have node body...
12486         var div = document.createElement('div');
12487         div.appendChild(node);
12488         tpl.dom = node;
12489         // this has the unfortunate side effect of converting tagged attributes
12490         // eg. href="{...}" into %7C...%7D
12491         // this has been fixed by searching for those combo's although it's a bit hacky..
12492         
12493         
12494         tpl.body = div.innerHTML;
12495         
12496         
12497          
12498         tpl.id = tpl.uid;
12499         switch(tpl.attr) {
12500             case 'for' :
12501                 switch (tpl.value) {
12502                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12503                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12504                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12505                 }
12506                 break;
12507             
12508             case 'exec':
12509                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12510                 break;
12511             
12512             case 'if':     
12513                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12514                 break;
12515             
12516             case 'name':
12517                 tpl.id  = tpl.value; // replace non characters???
12518                 break;
12519             
12520         }
12521         
12522         
12523         this.tpls.push(tpl);
12524         
12525         
12526         
12527     },
12528     
12529     
12530     
12531     
12532     /**
12533      * Compile a segment of the template into a 'sub-template'
12534      *
12535      * 
12536      * 
12537      *
12538      */
12539     compileTpl : function(tpl)
12540     {
12541         var fm = Roo.util.Format;
12542         var useF = this.disableFormats !== true;
12543         
12544         var sep = Roo.isGecko ? "+\n" : ",\n";
12545         
12546         var undef = function(str) {
12547             Roo.debug && Roo.log("Property not found :"  + str);
12548             return '';
12549         };
12550           
12551         //Roo.log(tpl.body);
12552         
12553         
12554         
12555         var fn = function(m, lbrace, name, format, args)
12556         {
12557             //Roo.log("ARGS");
12558             //Roo.log(arguments);
12559             args = args ? args.replace(/\\'/g,"'") : args;
12560             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12561             if (typeof(format) == 'undefined') {
12562                 format =  'htmlEncode'; 
12563             }
12564             if (format == 'raw' ) {
12565                 format = false;
12566             }
12567             
12568             if(name.substr(0, 6) == 'domtpl'){
12569                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12570             }
12571             
12572             // build an array of options to determine if value is undefined..
12573             
12574             // basically get 'xxxx.yyyy' then do
12575             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12576             //    (function () { Roo.log("Property not found"); return ''; })() :
12577             //    ......
12578             
12579             var udef_ar = [];
12580             var lookfor = '';
12581             Roo.each(name.split('.'), function(st) {
12582                 lookfor += (lookfor.length ? '.': '') + st;
12583                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12584             });
12585             
12586             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12587             
12588             
12589             if(format && useF){
12590                 
12591                 args = args ? ',' + args : "";
12592                  
12593                 if(format.substr(0, 5) != "this."){
12594                     format = "fm." + format + '(';
12595                 }else{
12596                     format = 'this.call("'+ format.substr(5) + '", ';
12597                     args = ", values";
12598                 }
12599                 
12600                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12601             }
12602              
12603             if (args && args.length) {
12604                 // called with xxyx.yuu:(test,test)
12605                 // change to ()
12606                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12607             }
12608             // raw.. - :raw modifier..
12609             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12610             
12611         };
12612         var body;
12613         // branched to use + in gecko and [].join() in others
12614         if(Roo.isGecko){
12615             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12616                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12617                     "';};};";
12618         }else{
12619             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12620             body.push(tpl.body.replace(/(\r\n|\n)/g,
12621                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12622             body.push("'].join('');};};");
12623             body = body.join('');
12624         }
12625         
12626         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12627        
12628         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12629         eval(body);
12630         
12631         return this;
12632     },
12633      
12634     /**
12635      * same as applyTemplate, except it's done to one of the subTemplates
12636      * when using named templates, you can do:
12637      *
12638      * var str = pl.applySubTemplate('your-name', values);
12639      *
12640      * 
12641      * @param {Number} id of the template
12642      * @param {Object} values to apply to template
12643      * @param {Object} parent (normaly the instance of this object)
12644      */
12645     applySubTemplate : function(id, values, parent)
12646     {
12647         
12648         
12649         var t = this.tpls[id];
12650         
12651         
12652         try { 
12653             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12654                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12655                 return '';
12656             }
12657         } catch(e) {
12658             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12659             Roo.log(values);
12660           
12661             return '';
12662         }
12663         try { 
12664             
12665             if(t.execCall && t.execCall.call(this, values, parent)){
12666                 return '';
12667             }
12668         } catch(e) {
12669             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12670             Roo.log(values);
12671             return '';
12672         }
12673         
12674         try {
12675             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12676             parent = t.target ? values : parent;
12677             if(t.forCall && vs instanceof Array){
12678                 var buf = [];
12679                 for(var i = 0, len = vs.length; i < len; i++){
12680                     try {
12681                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12682                     } catch (e) {
12683                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12684                         Roo.log(e.body);
12685                         //Roo.log(t.compiled);
12686                         Roo.log(vs[i]);
12687                     }   
12688                 }
12689                 return buf.join('');
12690             }
12691         } catch (e) {
12692             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12693             Roo.log(values);
12694             return '';
12695         }
12696         try {
12697             return t.compiled.call(this, vs, parent);
12698         } catch (e) {
12699             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12700             Roo.log(e.body);
12701             //Roo.log(t.compiled);
12702             Roo.log(values);
12703             return '';
12704         }
12705     },
12706
12707    
12708
12709     applyTemplate : function(values){
12710         return this.master.compiled.call(this, values, {});
12711         //var s = this.subs;
12712     },
12713
12714     apply : function(){
12715         return this.applyTemplate.apply(this, arguments);
12716     }
12717
12718  });
12719
12720 Roo.DomTemplate.from = function(el){
12721     el = Roo.getDom(el);
12722     return new Roo.Domtemplate(el.value || el.innerHTML);
12723 };/*
12724  * Based on:
12725  * Ext JS Library 1.1.1
12726  * Copyright(c) 2006-2007, Ext JS, LLC.
12727  *
12728  * Originally Released Under LGPL - original licence link has changed is not relivant.
12729  *
12730  * Fork - LGPL
12731  * <script type="text/javascript">
12732  */
12733
12734 /**
12735  * @class Roo.util.DelayedTask
12736  * Provides a convenient method of performing setTimeout where a new
12737  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12738  * You can use this class to buffer
12739  * the keypress events for a certain number of milliseconds, and perform only if they stop
12740  * for that amount of time.
12741  * @constructor The parameters to this constructor serve as defaults and are not required.
12742  * @param {Function} fn (optional) The default function to timeout
12743  * @param {Object} scope (optional) The default scope of that timeout
12744  * @param {Array} args (optional) The default Array of arguments
12745  */
12746 Roo.util.DelayedTask = function(fn, scope, args){
12747     var id = null, d, t;
12748
12749     var call = function(){
12750         var now = new Date().getTime();
12751         if(now - t >= d){
12752             clearInterval(id);
12753             id = null;
12754             fn.apply(scope, args || []);
12755         }
12756     };
12757     /**
12758      * Cancels any pending timeout and queues a new one
12759      * @param {Number} delay The milliseconds to delay
12760      * @param {Function} newFn (optional) Overrides function passed to constructor
12761      * @param {Object} newScope (optional) Overrides scope passed to constructor
12762      * @param {Array} newArgs (optional) Overrides args passed to constructor
12763      */
12764     this.delay = function(delay, newFn, newScope, newArgs){
12765         if(id && delay != d){
12766             this.cancel();
12767         }
12768         d = delay;
12769         t = new Date().getTime();
12770         fn = newFn || fn;
12771         scope = newScope || scope;
12772         args = newArgs || args;
12773         if(!id){
12774             id = setInterval(call, d);
12775         }
12776     };
12777
12778     /**
12779      * Cancel the last queued timeout
12780      */
12781     this.cancel = function(){
12782         if(id){
12783             clearInterval(id);
12784             id = null;
12785         }
12786     };
12787 };/*
12788  * Based on:
12789  * Ext JS Library 1.1.1
12790  * Copyright(c) 2006-2007, Ext JS, LLC.
12791  *
12792  * Originally Released Under LGPL - original licence link has changed is not relivant.
12793  *
12794  * Fork - LGPL
12795  * <script type="text/javascript">
12796  */
12797  
12798  
12799 Roo.util.TaskRunner = function(interval){
12800     interval = interval || 10;
12801     var tasks = [], removeQueue = [];
12802     var id = 0;
12803     var running = false;
12804
12805     var stopThread = function(){
12806         running = false;
12807         clearInterval(id);
12808         id = 0;
12809     };
12810
12811     var startThread = function(){
12812         if(!running){
12813             running = true;
12814             id = setInterval(runTasks, interval);
12815         }
12816     };
12817
12818     var removeTask = function(task){
12819         removeQueue.push(task);
12820         if(task.onStop){
12821             task.onStop();
12822         }
12823     };
12824
12825     var runTasks = function(){
12826         if(removeQueue.length > 0){
12827             for(var i = 0, len = removeQueue.length; i < len; i++){
12828                 tasks.remove(removeQueue[i]);
12829             }
12830             removeQueue = [];
12831             if(tasks.length < 1){
12832                 stopThread();
12833                 return;
12834             }
12835         }
12836         var now = new Date().getTime();
12837         for(var i = 0, len = tasks.length; i < len; ++i){
12838             var t = tasks[i];
12839             var itime = now - t.taskRunTime;
12840             if(t.interval <= itime){
12841                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12842                 t.taskRunTime = now;
12843                 if(rt === false || t.taskRunCount === t.repeat){
12844                     removeTask(t);
12845                     return;
12846                 }
12847             }
12848             if(t.duration && t.duration <= (now - t.taskStartTime)){
12849                 removeTask(t);
12850             }
12851         }
12852     };
12853
12854     /**
12855      * Queues a new task.
12856      * @param {Object} task
12857      */
12858     this.start = function(task){
12859         tasks.push(task);
12860         task.taskStartTime = new Date().getTime();
12861         task.taskRunTime = 0;
12862         task.taskRunCount = 0;
12863         startThread();
12864         return task;
12865     };
12866
12867     this.stop = function(task){
12868         removeTask(task);
12869         return task;
12870     };
12871
12872     this.stopAll = function(){
12873         stopThread();
12874         for(var i = 0, len = tasks.length; i < len; i++){
12875             if(tasks[i].onStop){
12876                 tasks[i].onStop();
12877             }
12878         }
12879         tasks = [];
12880         removeQueue = [];
12881     };
12882 };
12883
12884 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12885  * Based on:
12886  * Ext JS Library 1.1.1
12887  * Copyright(c) 2006-2007, Ext JS, LLC.
12888  *
12889  * Originally Released Under LGPL - original licence link has changed is not relivant.
12890  *
12891  * Fork - LGPL
12892  * <script type="text/javascript">
12893  */
12894
12895  
12896 /**
12897  * @class Roo.util.MixedCollection
12898  * @extends Roo.util.Observable
12899  * A Collection class that maintains both numeric indexes and keys and exposes events.
12900  * @constructor
12901  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12902  * collection (defaults to false)
12903  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12904  * and return the key value for that item.  This is used when available to look up the key on items that
12905  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12906  * equivalent to providing an implementation for the {@link #getKey} method.
12907  */
12908 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12909     this.items = [];
12910     this.map = {};
12911     this.keys = [];
12912     this.length = 0;
12913     this.addEvents({
12914         /**
12915          * @event clear
12916          * Fires when the collection is cleared.
12917          */
12918         "clear" : true,
12919         /**
12920          * @event add
12921          * Fires when an item is added to the collection.
12922          * @param {Number} index The index at which the item was added.
12923          * @param {Object} o The item added.
12924          * @param {String} key The key associated with the added item.
12925          */
12926         "add" : true,
12927         /**
12928          * @event replace
12929          * Fires when an item is replaced in the collection.
12930          * @param {String} key he key associated with the new added.
12931          * @param {Object} old The item being replaced.
12932          * @param {Object} new The new item.
12933          */
12934         "replace" : true,
12935         /**
12936          * @event remove
12937          * Fires when an item is removed from the collection.
12938          * @param {Object} o The item being removed.
12939          * @param {String} key (optional) The key associated with the removed item.
12940          */
12941         "remove" : true,
12942         "sort" : true
12943     });
12944     this.allowFunctions = allowFunctions === true;
12945     if(keyFn){
12946         this.getKey = keyFn;
12947     }
12948     Roo.util.MixedCollection.superclass.constructor.call(this);
12949 };
12950
12951 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12952     allowFunctions : false,
12953     
12954 /**
12955  * Adds an item to the collection.
12956  * @param {String} key The key to associate with the item
12957  * @param {Object} o The item to add.
12958  * @return {Object} The item added.
12959  */
12960     add : function(key, o){
12961         if(arguments.length == 1){
12962             o = arguments[0];
12963             key = this.getKey(o);
12964         }
12965         if(typeof key == "undefined" || key === null){
12966             this.length++;
12967             this.items.push(o);
12968             this.keys.push(null);
12969         }else{
12970             var old = this.map[key];
12971             if(old){
12972                 return this.replace(key, o);
12973             }
12974             this.length++;
12975             this.items.push(o);
12976             this.map[key] = o;
12977             this.keys.push(key);
12978         }
12979         this.fireEvent("add", this.length-1, o, key);
12980         return o;
12981     },
12982        
12983 /**
12984   * MixedCollection has a generic way to fetch keys if you implement getKey.
12985 <pre><code>
12986 // normal way
12987 var mc = new Roo.util.MixedCollection();
12988 mc.add(someEl.dom.id, someEl);
12989 mc.add(otherEl.dom.id, otherEl);
12990 //and so on
12991
12992 // using getKey
12993 var mc = new Roo.util.MixedCollection();
12994 mc.getKey = function(el){
12995    return el.dom.id;
12996 };
12997 mc.add(someEl);
12998 mc.add(otherEl);
12999
13000 // or via the constructor
13001 var mc = new Roo.util.MixedCollection(false, function(el){
13002    return el.dom.id;
13003 });
13004 mc.add(someEl);
13005 mc.add(otherEl);
13006 </code></pre>
13007  * @param o {Object} The item for which to find the key.
13008  * @return {Object} The key for the passed item.
13009  */
13010     getKey : function(o){
13011          return o.id; 
13012     },
13013    
13014 /**
13015  * Replaces an item in the collection.
13016  * @param {String} key The key associated with the item to replace, or the item to replace.
13017  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13018  * @return {Object}  The new item.
13019  */
13020     replace : function(key, o){
13021         if(arguments.length == 1){
13022             o = arguments[0];
13023             key = this.getKey(o);
13024         }
13025         var old = this.item(key);
13026         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13027              return this.add(key, o);
13028         }
13029         var index = this.indexOfKey(key);
13030         this.items[index] = o;
13031         this.map[key] = o;
13032         this.fireEvent("replace", key, old, o);
13033         return o;
13034     },
13035    
13036 /**
13037  * Adds all elements of an Array or an Object to the collection.
13038  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13039  * an Array of values, each of which are added to the collection.
13040  */
13041     addAll : function(objs){
13042         if(arguments.length > 1 || objs instanceof Array){
13043             var args = arguments.length > 1 ? arguments : objs;
13044             for(var i = 0, len = args.length; i < len; i++){
13045                 this.add(args[i]);
13046             }
13047         }else{
13048             for(var key in objs){
13049                 if(this.allowFunctions || typeof objs[key] != "function"){
13050                     this.add(key, objs[key]);
13051                 }
13052             }
13053         }
13054     },
13055    
13056 /**
13057  * Executes the specified function once for every item in the collection, passing each
13058  * item as the first and only parameter. returning false from the function will stop the iteration.
13059  * @param {Function} fn The function to execute for each item.
13060  * @param {Object} scope (optional) The scope in which to execute the function.
13061  */
13062     each : function(fn, scope){
13063         var items = [].concat(this.items); // each safe for removal
13064         for(var i = 0, len = items.length; i < len; i++){
13065             if(fn.call(scope || items[i], items[i], i, len) === false){
13066                 break;
13067             }
13068         }
13069     },
13070    
13071 /**
13072  * Executes the specified function once for every key in the collection, passing each
13073  * key, and its associated item as the first two parameters.
13074  * @param {Function} fn The function to execute for each item.
13075  * @param {Object} scope (optional) The scope in which to execute the function.
13076  */
13077     eachKey : function(fn, scope){
13078         for(var i = 0, len = this.keys.length; i < len; i++){
13079             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13080         }
13081     },
13082    
13083 /**
13084  * Returns the first item in the collection which elicits a true return value from the
13085  * passed selection function.
13086  * @param {Function} fn The selection function to execute for each item.
13087  * @param {Object} scope (optional) The scope in which to execute the function.
13088  * @return {Object} The first item in the collection which returned true from the selection function.
13089  */
13090     find : function(fn, scope){
13091         for(var i = 0, len = this.items.length; i < len; i++){
13092             if(fn.call(scope || window, this.items[i], this.keys[i])){
13093                 return this.items[i];
13094             }
13095         }
13096         return null;
13097     },
13098    
13099 /**
13100  * Inserts an item at the specified index in the collection.
13101  * @param {Number} index The index to insert the item at.
13102  * @param {String} key The key to associate with the new item, or the item itself.
13103  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13104  * @return {Object} The item inserted.
13105  */
13106     insert : function(index, key, o){
13107         if(arguments.length == 2){
13108             o = arguments[1];
13109             key = this.getKey(o);
13110         }
13111         if(index >= this.length){
13112             return this.add(key, o);
13113         }
13114         this.length++;
13115         this.items.splice(index, 0, o);
13116         if(typeof key != "undefined" && key != null){
13117             this.map[key] = o;
13118         }
13119         this.keys.splice(index, 0, key);
13120         this.fireEvent("add", index, o, key);
13121         return o;
13122     },
13123    
13124 /**
13125  * Removed an item from the collection.
13126  * @param {Object} o The item to remove.
13127  * @return {Object} The item removed.
13128  */
13129     remove : function(o){
13130         return this.removeAt(this.indexOf(o));
13131     },
13132    
13133 /**
13134  * Remove an item from a specified index in the collection.
13135  * @param {Number} index The index within the collection of the item to remove.
13136  */
13137     removeAt : function(index){
13138         if(index < this.length && index >= 0){
13139             this.length--;
13140             var o = this.items[index];
13141             this.items.splice(index, 1);
13142             var key = this.keys[index];
13143             if(typeof key != "undefined"){
13144                 delete this.map[key];
13145             }
13146             this.keys.splice(index, 1);
13147             this.fireEvent("remove", o, key);
13148         }
13149     },
13150    
13151 /**
13152  * Removed an item associated with the passed key fom the collection.
13153  * @param {String} key The key of the item to remove.
13154  */
13155     removeKey : function(key){
13156         return this.removeAt(this.indexOfKey(key));
13157     },
13158    
13159 /**
13160  * Returns the number of items in the collection.
13161  * @return {Number} the number of items in the collection.
13162  */
13163     getCount : function(){
13164         return this.length; 
13165     },
13166    
13167 /**
13168  * Returns index within the collection of the passed Object.
13169  * @param {Object} o The item to find the index of.
13170  * @return {Number} index of the item.
13171  */
13172     indexOf : function(o){
13173         if(!this.items.indexOf){
13174             for(var i = 0, len = this.items.length; i < len; i++){
13175                 if(this.items[i] == o) {
13176                     return i;
13177                 }
13178             }
13179             return -1;
13180         }else{
13181             return this.items.indexOf(o);
13182         }
13183     },
13184    
13185 /**
13186  * Returns index within the collection of the passed key.
13187  * @param {String} key The key to find the index of.
13188  * @return {Number} index of the key.
13189  */
13190     indexOfKey : function(key){
13191         if(!this.keys.indexOf){
13192             for(var i = 0, len = this.keys.length; i < len; i++){
13193                 if(this.keys[i] == key) {
13194                     return i;
13195                 }
13196             }
13197             return -1;
13198         }else{
13199             return this.keys.indexOf(key);
13200         }
13201     },
13202    
13203 /**
13204  * Returns the item associated with the passed key OR index. Key has priority over index.
13205  * @param {String/Number} key The key or index of the item.
13206  * @return {Object} The item associated with the passed key.
13207  */
13208     item : function(key){
13209         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13210         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13211     },
13212     
13213 /**
13214  * Returns the item at the specified index.
13215  * @param {Number} index The index of the item.
13216  * @return {Object}
13217  */
13218     itemAt : function(index){
13219         return this.items[index];
13220     },
13221     
13222 /**
13223  * Returns the item associated with the passed key.
13224  * @param {String/Number} key The key of the item.
13225  * @return {Object} The item associated with the passed key.
13226  */
13227     key : function(key){
13228         return this.map[key];
13229     },
13230    
13231 /**
13232  * Returns true if the collection contains the passed Object as an item.
13233  * @param {Object} o  The Object to look for in the collection.
13234  * @return {Boolean} True if the collection contains the Object as an item.
13235  */
13236     contains : function(o){
13237         return this.indexOf(o) != -1;
13238     },
13239    
13240 /**
13241  * Returns true if the collection contains the passed Object as a key.
13242  * @param {String} key The key to look for in the collection.
13243  * @return {Boolean} True if the collection contains the Object as a key.
13244  */
13245     containsKey : function(key){
13246         return typeof this.map[key] != "undefined";
13247     },
13248    
13249 /**
13250  * Removes all items from the collection.
13251  */
13252     clear : function(){
13253         this.length = 0;
13254         this.items = [];
13255         this.keys = [];
13256         this.map = {};
13257         this.fireEvent("clear");
13258     },
13259    
13260 /**
13261  * Returns the first item in the collection.
13262  * @return {Object} the first item in the collection..
13263  */
13264     first : function(){
13265         return this.items[0]; 
13266     },
13267    
13268 /**
13269  * Returns the last item in the collection.
13270  * @return {Object} the last item in the collection..
13271  */
13272     last : function(){
13273         return this.items[this.length-1];   
13274     },
13275     
13276     _sort : function(property, dir, fn){
13277         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13278         fn = fn || function(a, b){
13279             return a-b;
13280         };
13281         var c = [], k = this.keys, items = this.items;
13282         for(var i = 0, len = items.length; i < len; i++){
13283             c[c.length] = {key: k[i], value: items[i], index: i};
13284         }
13285         c.sort(function(a, b){
13286             var v = fn(a[property], b[property]) * dsc;
13287             if(v == 0){
13288                 v = (a.index < b.index ? -1 : 1);
13289             }
13290             return v;
13291         });
13292         for(var i = 0, len = c.length; i < len; i++){
13293             items[i] = c[i].value;
13294             k[i] = c[i].key;
13295         }
13296         this.fireEvent("sort", this);
13297     },
13298     
13299     /**
13300      * Sorts this collection with the passed comparison function
13301      * @param {String} direction (optional) "ASC" or "DESC"
13302      * @param {Function} fn (optional) comparison function
13303      */
13304     sort : function(dir, fn){
13305         this._sort("value", dir, fn);
13306     },
13307     
13308     /**
13309      * Sorts this collection by keys
13310      * @param {String} direction (optional) "ASC" or "DESC"
13311      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13312      */
13313     keySort : function(dir, fn){
13314         this._sort("key", dir, fn || function(a, b){
13315             return String(a).toUpperCase()-String(b).toUpperCase();
13316         });
13317     },
13318     
13319     /**
13320      * Returns a range of items in this collection
13321      * @param {Number} startIndex (optional) defaults to 0
13322      * @param {Number} endIndex (optional) default to the last item
13323      * @return {Array} An array of items
13324      */
13325     getRange : function(start, end){
13326         var items = this.items;
13327         if(items.length < 1){
13328             return [];
13329         }
13330         start = start || 0;
13331         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13332         var r = [];
13333         if(start <= end){
13334             for(var i = start; i <= end; i++) {
13335                     r[r.length] = items[i];
13336             }
13337         }else{
13338             for(var i = start; i >= end; i--) {
13339                     r[r.length] = items[i];
13340             }
13341         }
13342         return r;
13343     },
13344         
13345     /**
13346      * Filter the <i>objects</i> in this collection by a specific property. 
13347      * Returns a new collection that has been filtered.
13348      * @param {String} property A property on your objects
13349      * @param {String/RegExp} value Either string that the property values 
13350      * should start with or a RegExp to test against the property
13351      * @return {MixedCollection} The new filtered collection
13352      */
13353     filter : function(property, value){
13354         if(!value.exec){ // not a regex
13355             value = String(value);
13356             if(value.length == 0){
13357                 return this.clone();
13358             }
13359             value = new RegExp("^" + Roo.escapeRe(value), "i");
13360         }
13361         return this.filterBy(function(o){
13362             return o && value.test(o[property]);
13363         });
13364         },
13365     
13366     /**
13367      * Filter by a function. * Returns a new collection that has been filtered.
13368      * The passed function will be called with each 
13369      * object in the collection. If the function returns true, the value is included 
13370      * otherwise it is filtered.
13371      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13372      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13373      * @return {MixedCollection} The new filtered collection
13374      */
13375     filterBy : function(fn, scope){
13376         var r = new Roo.util.MixedCollection();
13377         r.getKey = this.getKey;
13378         var k = this.keys, it = this.items;
13379         for(var i = 0, len = it.length; i < len; i++){
13380             if(fn.call(scope||this, it[i], k[i])){
13381                                 r.add(k[i], it[i]);
13382                         }
13383         }
13384         return r;
13385     },
13386     
13387     /**
13388      * Creates a duplicate of this collection
13389      * @return {MixedCollection}
13390      */
13391     clone : function(){
13392         var r = new Roo.util.MixedCollection();
13393         var k = this.keys, it = this.items;
13394         for(var i = 0, len = it.length; i < len; i++){
13395             r.add(k[i], it[i]);
13396         }
13397         r.getKey = this.getKey;
13398         return r;
13399     }
13400 });
13401 /**
13402  * Returns the item associated with the passed key or index.
13403  * @method
13404  * @param {String/Number} key The key or index of the item.
13405  * @return {Object} The item associated with the passed key.
13406  */
13407 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13408  * Based on:
13409  * Ext JS Library 1.1.1
13410  * Copyright(c) 2006-2007, Ext JS, LLC.
13411  *
13412  * Originally Released Under LGPL - original licence link has changed is not relivant.
13413  *
13414  * Fork - LGPL
13415  * <script type="text/javascript">
13416  */
13417 /**
13418  * @class Roo.util.JSON
13419  * Modified version of Douglas Crockford"s json.js that doesn"t
13420  * mess with the Object prototype 
13421  * http://www.json.org/js.html
13422  * @singleton
13423  */
13424 Roo.util.JSON = new (function(){
13425     var useHasOwn = {}.hasOwnProperty ? true : false;
13426     
13427     // crashes Safari in some instances
13428     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13429     
13430     var pad = function(n) {
13431         return n < 10 ? "0" + n : n;
13432     };
13433     
13434     var m = {
13435         "\b": '\\b',
13436         "\t": '\\t',
13437         "\n": '\\n',
13438         "\f": '\\f',
13439         "\r": '\\r',
13440         '"' : '\\"',
13441         "\\": '\\\\'
13442     };
13443
13444     var encodeString = function(s){
13445         if (/["\\\x00-\x1f]/.test(s)) {
13446             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13447                 var c = m[b];
13448                 if(c){
13449                     return c;
13450                 }
13451                 c = b.charCodeAt();
13452                 return "\\u00" +
13453                     Math.floor(c / 16).toString(16) +
13454                     (c % 16).toString(16);
13455             }) + '"';
13456         }
13457         return '"' + s + '"';
13458     };
13459     
13460     var encodeArray = function(o){
13461         var a = ["["], b, i, l = o.length, v;
13462             for (i = 0; i < l; i += 1) {
13463                 v = o[i];
13464                 switch (typeof v) {
13465                     case "undefined":
13466                     case "function":
13467                     case "unknown":
13468                         break;
13469                     default:
13470                         if (b) {
13471                             a.push(',');
13472                         }
13473                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13474                         b = true;
13475                 }
13476             }
13477             a.push("]");
13478             return a.join("");
13479     };
13480     
13481     var encodeDate = function(o){
13482         return '"' + o.getFullYear() + "-" +
13483                 pad(o.getMonth() + 1) + "-" +
13484                 pad(o.getDate()) + "T" +
13485                 pad(o.getHours()) + ":" +
13486                 pad(o.getMinutes()) + ":" +
13487                 pad(o.getSeconds()) + '"';
13488     };
13489     
13490     /**
13491      * Encodes an Object, Array or other value
13492      * @param {Mixed} o The variable to encode
13493      * @return {String} The JSON string
13494      */
13495     this.encode = function(o)
13496     {
13497         // should this be extended to fully wrap stringify..
13498         
13499         if(typeof o == "undefined" || o === null){
13500             return "null";
13501         }else if(o instanceof Array){
13502             return encodeArray(o);
13503         }else if(o instanceof Date){
13504             return encodeDate(o);
13505         }else if(typeof o == "string"){
13506             return encodeString(o);
13507         }else if(typeof o == "number"){
13508             return isFinite(o) ? String(o) : "null";
13509         }else if(typeof o == "boolean"){
13510             return String(o);
13511         }else {
13512             var a = ["{"], b, i, v;
13513             for (i in o) {
13514                 if(!useHasOwn || o.hasOwnProperty(i)) {
13515                     v = o[i];
13516                     switch (typeof v) {
13517                     case "undefined":
13518                     case "function":
13519                     case "unknown":
13520                         break;
13521                     default:
13522                         if(b){
13523                             a.push(',');
13524                         }
13525                         a.push(this.encode(i), ":",
13526                                 v === null ? "null" : this.encode(v));
13527                         b = true;
13528                     }
13529                 }
13530             }
13531             a.push("}");
13532             return a.join("");
13533         }
13534     };
13535     
13536     /**
13537      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13538      * @param {String} json The JSON string
13539      * @return {Object} The resulting object
13540      */
13541     this.decode = function(json){
13542         
13543         return  /** eval:var:json */ eval("(" + json + ')');
13544     };
13545 })();
13546 /** 
13547  * Shorthand for {@link Roo.util.JSON#encode}
13548  * @member Roo encode 
13549  * @method */
13550 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13551 /** 
13552  * Shorthand for {@link Roo.util.JSON#decode}
13553  * @member Roo decode 
13554  * @method */
13555 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13556 /*
13557  * Based on:
13558  * Ext JS Library 1.1.1
13559  * Copyright(c) 2006-2007, Ext JS, LLC.
13560  *
13561  * Originally Released Under LGPL - original licence link has changed is not relivant.
13562  *
13563  * Fork - LGPL
13564  * <script type="text/javascript">
13565  */
13566  
13567 /**
13568  * @class Roo.util.Format
13569  * Reusable data formatting functions
13570  * @singleton
13571  */
13572 Roo.util.Format = function(){
13573     var trimRe = /^\s+|\s+$/g;
13574     return {
13575         /**
13576          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13577          * @param {String} value The string to truncate
13578          * @param {Number} length The maximum length to allow before truncating
13579          * @return {String} The converted text
13580          */
13581         ellipsis : function(value, len){
13582             if(value && value.length > len){
13583                 return value.substr(0, len-3)+"...";
13584             }
13585             return value;
13586         },
13587
13588         /**
13589          * Checks a reference and converts it to empty string if it is undefined
13590          * @param {Mixed} value Reference to check
13591          * @return {Mixed} Empty string if converted, otherwise the original value
13592          */
13593         undef : function(value){
13594             return typeof value != "undefined" ? value : "";
13595         },
13596
13597         /**
13598          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13599          * @param {String} value The string to encode
13600          * @return {String} The encoded text
13601          */
13602         htmlEncode : function(value){
13603             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13604         },
13605
13606         /**
13607          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13608          * @param {String} value The string to decode
13609          * @return {String} The decoded text
13610          */
13611         htmlDecode : function(value){
13612             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13613         },
13614
13615         /**
13616          * Trims any whitespace from either side of a string
13617          * @param {String} value The text to trim
13618          * @return {String} The trimmed text
13619          */
13620         trim : function(value){
13621             return String(value).replace(trimRe, "");
13622         },
13623
13624         /**
13625          * Returns a substring from within an original string
13626          * @param {String} value The original text
13627          * @param {Number} start The start index of the substring
13628          * @param {Number} length The length of the substring
13629          * @return {String} The substring
13630          */
13631         substr : function(value, start, length){
13632             return String(value).substr(start, length);
13633         },
13634
13635         /**
13636          * Converts a string to all lower case letters
13637          * @param {String} value The text to convert
13638          * @return {String} The converted text
13639          */
13640         lowercase : function(value){
13641             return String(value).toLowerCase();
13642         },
13643
13644         /**
13645          * Converts a string to all upper case letters
13646          * @param {String} value The text to convert
13647          * @return {String} The converted text
13648          */
13649         uppercase : function(value){
13650             return String(value).toUpperCase();
13651         },
13652
13653         /**
13654          * Converts the first character only of a string to upper case
13655          * @param {String} value The text to convert
13656          * @return {String} The converted text
13657          */
13658         capitalize : function(value){
13659             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13660         },
13661
13662         // private
13663         call : function(value, fn){
13664             if(arguments.length > 2){
13665                 var args = Array.prototype.slice.call(arguments, 2);
13666                 args.unshift(value);
13667                  
13668                 return /** eval:var:value */  eval(fn).apply(window, args);
13669             }else{
13670                 /** eval:var:value */
13671                 return /** eval:var:value */ eval(fn).call(window, value);
13672             }
13673         },
13674
13675        
13676         /**
13677          * safer version of Math.toFixed..??/
13678          * @param {Number/String} value The numeric value to format
13679          * @param {Number/String} value Decimal places 
13680          * @return {String} The formatted currency string
13681          */
13682         toFixed : function(v, n)
13683         {
13684             // why not use to fixed - precision is buggered???
13685             if (!n) {
13686                 return Math.round(v-0);
13687             }
13688             var fact = Math.pow(10,n+1);
13689             v = (Math.round((v-0)*fact))/fact;
13690             var z = (''+fact).substring(2);
13691             if (v == Math.floor(v)) {
13692                 return Math.floor(v) + '.' + z;
13693             }
13694             
13695             // now just padd decimals..
13696             var ps = String(v).split('.');
13697             var fd = (ps[1] + z);
13698             var r = fd.substring(0,n); 
13699             var rm = fd.substring(n); 
13700             if (rm < 5) {
13701                 return ps[0] + '.' + r;
13702             }
13703             r*=1; // turn it into a number;
13704             r++;
13705             if (String(r).length != n) {
13706                 ps[0]*=1;
13707                 ps[0]++;
13708                 r = String(r).substring(1); // chop the end off.
13709             }
13710             
13711             return ps[0] + '.' + r;
13712              
13713         },
13714         
13715         /**
13716          * Format a number as US currency
13717          * @param {Number/String} value The numeric value to format
13718          * @return {String} The formatted currency string
13719          */
13720         usMoney : function(v){
13721             return '$' + Roo.util.Format.number(v);
13722         },
13723         
13724         /**
13725          * Format a number
13726          * eventually this should probably emulate php's number_format
13727          * @param {Number/String} value The numeric value to format
13728          * @param {Number} decimals number of decimal places
13729          * @return {String} The formatted currency string
13730          */
13731         number : function(v,decimals)
13732         {
13733             // multiply and round.
13734             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13735             var mul = Math.pow(10, decimals);
13736             var zero = String(mul).substring(1);
13737             v = (Math.round((v-0)*mul))/mul;
13738             
13739             // if it's '0' number.. then
13740             
13741             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13742             v = String(v);
13743             var ps = v.split('.');
13744             var whole = ps[0];
13745             
13746             
13747             var r = /(\d+)(\d{3})/;
13748             // add comma's
13749             while (r.test(whole)) {
13750                 whole = whole.replace(r, '$1' + ',' + '$2');
13751             }
13752             
13753             
13754             var sub = ps[1] ?
13755                     // has decimals..
13756                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13757                     // does not have decimals
13758                     (decimals ? ('.' + zero) : '');
13759             
13760             
13761             return whole + sub ;
13762         },
13763         
13764         /**
13765          * Parse a value into a formatted date using the specified format pattern.
13766          * @param {Mixed} value The value to format
13767          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13768          * @return {String} The formatted date string
13769          */
13770         date : function(v, format){
13771             if(!v){
13772                 return "";
13773             }
13774             if(!(v instanceof Date)){
13775                 v = new Date(Date.parse(v));
13776             }
13777             return v.dateFormat(format || Roo.util.Format.defaults.date);
13778         },
13779
13780         /**
13781          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13782          * @param {String} format Any valid date format string
13783          * @return {Function} The date formatting function
13784          */
13785         dateRenderer : function(format){
13786             return function(v){
13787                 return Roo.util.Format.date(v, format);  
13788             };
13789         },
13790
13791         // private
13792         stripTagsRE : /<\/?[^>]+>/gi,
13793         
13794         /**
13795          * Strips all HTML tags
13796          * @param {Mixed} value The text from which to strip tags
13797          * @return {String} The stripped text
13798          */
13799         stripTags : function(v){
13800             return !v ? v : String(v).replace(this.stripTagsRE, "");
13801         }
13802     };
13803 }();
13804 Roo.util.Format.defaults = {
13805     date : 'd/M/Y'
13806 };/*
13807  * Based on:
13808  * Ext JS Library 1.1.1
13809  * Copyright(c) 2006-2007, Ext JS, LLC.
13810  *
13811  * Originally Released Under LGPL - original licence link has changed is not relivant.
13812  *
13813  * Fork - LGPL
13814  * <script type="text/javascript">
13815  */
13816
13817
13818  
13819
13820 /**
13821  * @class Roo.MasterTemplate
13822  * @extends Roo.Template
13823  * Provides a template that can have child templates. The syntax is:
13824 <pre><code>
13825 var t = new Roo.MasterTemplate(
13826         '&lt;select name="{name}"&gt;',
13827                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13828         '&lt;/select&gt;'
13829 );
13830 t.add('options', {value: 'foo', text: 'bar'});
13831 // or you can add multiple child elements in one shot
13832 t.addAll('options', [
13833     {value: 'foo', text: 'bar'},
13834     {value: 'foo2', text: 'bar2'},
13835     {value: 'foo3', text: 'bar3'}
13836 ]);
13837 // then append, applying the master template values
13838 t.append('my-form', {name: 'my-select'});
13839 </code></pre>
13840 * A name attribute for the child template is not required if you have only one child
13841 * template or you want to refer to them by index.
13842  */
13843 Roo.MasterTemplate = function(){
13844     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13845     this.originalHtml = this.html;
13846     var st = {};
13847     var m, re = this.subTemplateRe;
13848     re.lastIndex = 0;
13849     var subIndex = 0;
13850     while(m = re.exec(this.html)){
13851         var name = m[1], content = m[2];
13852         st[subIndex] = {
13853             name: name,
13854             index: subIndex,
13855             buffer: [],
13856             tpl : new Roo.Template(content)
13857         };
13858         if(name){
13859             st[name] = st[subIndex];
13860         }
13861         st[subIndex].tpl.compile();
13862         st[subIndex].tpl.call = this.call.createDelegate(this);
13863         subIndex++;
13864     }
13865     this.subCount = subIndex;
13866     this.subs = st;
13867 };
13868 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13869     /**
13870     * The regular expression used to match sub templates
13871     * @type RegExp
13872     * @property
13873     */
13874     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13875
13876     /**
13877      * Applies the passed values to a child template.
13878      * @param {String/Number} name (optional) The name or index of the child template
13879      * @param {Array/Object} values The values to be applied to the template
13880      * @return {MasterTemplate} this
13881      */
13882      add : function(name, values){
13883         if(arguments.length == 1){
13884             values = arguments[0];
13885             name = 0;
13886         }
13887         var s = this.subs[name];
13888         s.buffer[s.buffer.length] = s.tpl.apply(values);
13889         return this;
13890     },
13891
13892     /**
13893      * Applies all the passed values to a child template.
13894      * @param {String/Number} name (optional) The name or index of the child template
13895      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13896      * @param {Boolean} reset (optional) True to reset the template first
13897      * @return {MasterTemplate} this
13898      */
13899     fill : function(name, values, reset){
13900         var a = arguments;
13901         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13902             values = a[0];
13903             name = 0;
13904             reset = a[1];
13905         }
13906         if(reset){
13907             this.reset();
13908         }
13909         for(var i = 0, len = values.length; i < len; i++){
13910             this.add(name, values[i]);
13911         }
13912         return this;
13913     },
13914
13915     /**
13916      * Resets the template for reuse
13917      * @return {MasterTemplate} this
13918      */
13919      reset : function(){
13920         var s = this.subs;
13921         for(var i = 0; i < this.subCount; i++){
13922             s[i].buffer = [];
13923         }
13924         return this;
13925     },
13926
13927     applyTemplate : function(values){
13928         var s = this.subs;
13929         var replaceIndex = -1;
13930         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13931             return s[++replaceIndex].buffer.join("");
13932         });
13933         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13934     },
13935
13936     apply : function(){
13937         return this.applyTemplate.apply(this, arguments);
13938     },
13939
13940     compile : function(){return this;}
13941 });
13942
13943 /**
13944  * Alias for fill().
13945  * @method
13946  */
13947 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13948  /**
13949  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13950  * var tpl = Roo.MasterTemplate.from('element-id');
13951  * @param {String/HTMLElement} el
13952  * @param {Object} config
13953  * @static
13954  */
13955 Roo.MasterTemplate.from = function(el, config){
13956     el = Roo.getDom(el);
13957     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13958 };/*
13959  * Based on:
13960  * Ext JS Library 1.1.1
13961  * Copyright(c) 2006-2007, Ext JS, LLC.
13962  *
13963  * Originally Released Under LGPL - original licence link has changed is not relivant.
13964  *
13965  * Fork - LGPL
13966  * <script type="text/javascript">
13967  */
13968
13969  
13970 /**
13971  * @class Roo.util.CSS
13972  * Utility class for manipulating CSS rules
13973  * @singleton
13974  */
13975 Roo.util.CSS = function(){
13976         var rules = null;
13977         var doc = document;
13978
13979     var camelRe = /(-[a-z])/gi;
13980     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13981
13982    return {
13983    /**
13984     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13985     * tag and appended to the HEAD of the document.
13986     * @param {String|Object} cssText The text containing the css rules
13987     * @param {String} id An id to add to the stylesheet for later removal
13988     * @return {StyleSheet}
13989     */
13990     createStyleSheet : function(cssText, id){
13991         var ss;
13992         var head = doc.getElementsByTagName("head")[0];
13993         var nrules = doc.createElement("style");
13994         nrules.setAttribute("type", "text/css");
13995         if(id){
13996             nrules.setAttribute("id", id);
13997         }
13998         if (typeof(cssText) != 'string') {
13999             // support object maps..
14000             // not sure if this a good idea.. 
14001             // perhaps it should be merged with the general css handling
14002             // and handle js style props.
14003             var cssTextNew = [];
14004             for(var n in cssText) {
14005                 var citems = [];
14006                 for(var k in cssText[n]) {
14007                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14008                 }
14009                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14010                 
14011             }
14012             cssText = cssTextNew.join("\n");
14013             
14014         }
14015        
14016        
14017        if(Roo.isIE){
14018            head.appendChild(nrules);
14019            ss = nrules.styleSheet;
14020            ss.cssText = cssText;
14021        }else{
14022            try{
14023                 nrules.appendChild(doc.createTextNode(cssText));
14024            }catch(e){
14025                nrules.cssText = cssText; 
14026            }
14027            head.appendChild(nrules);
14028            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14029        }
14030        this.cacheStyleSheet(ss);
14031        return ss;
14032    },
14033
14034    /**
14035     * Removes a style or link tag by id
14036     * @param {String} id The id of the tag
14037     */
14038    removeStyleSheet : function(id){
14039        var existing = doc.getElementById(id);
14040        if(existing){
14041            existing.parentNode.removeChild(existing);
14042        }
14043    },
14044
14045    /**
14046     * Dynamically swaps an existing stylesheet reference for a new one
14047     * @param {String} id The id of an existing link tag to remove
14048     * @param {String} url The href of the new stylesheet to include
14049     */
14050    swapStyleSheet : function(id, url){
14051        this.removeStyleSheet(id);
14052        var ss = doc.createElement("link");
14053        ss.setAttribute("rel", "stylesheet");
14054        ss.setAttribute("type", "text/css");
14055        ss.setAttribute("id", id);
14056        ss.setAttribute("href", url);
14057        doc.getElementsByTagName("head")[0].appendChild(ss);
14058    },
14059    
14060    /**
14061     * Refresh the rule cache if you have dynamically added stylesheets
14062     * @return {Object} An object (hash) of rules indexed by selector
14063     */
14064    refreshCache : function(){
14065        return this.getRules(true);
14066    },
14067
14068    // private
14069    cacheStyleSheet : function(stylesheet){
14070        if(!rules){
14071            rules = {};
14072        }
14073        try{// try catch for cross domain access issue
14074            var ssRules = stylesheet.cssRules || stylesheet.rules;
14075            for(var j = ssRules.length-1; j >= 0; --j){
14076                rules[ssRules[j].selectorText] = ssRules[j];
14077            }
14078        }catch(e){}
14079    },
14080    
14081    /**
14082     * Gets all css rules for the document
14083     * @param {Boolean} refreshCache true to refresh the internal cache
14084     * @return {Object} An object (hash) of rules indexed by selector
14085     */
14086    getRules : function(refreshCache){
14087                 if(rules == null || refreshCache){
14088                         rules = {};
14089                         var ds = doc.styleSheets;
14090                         for(var i =0, len = ds.length; i < len; i++){
14091                             try{
14092                         this.cacheStyleSheet(ds[i]);
14093                     }catch(e){} 
14094                 }
14095                 }
14096                 return rules;
14097         },
14098         
14099         /**
14100     * Gets an an individual CSS rule by selector(s)
14101     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14102     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14103     * @return {CSSRule} The CSS rule or null if one is not found
14104     */
14105    getRule : function(selector, refreshCache){
14106                 var rs = this.getRules(refreshCache);
14107                 if(!(selector instanceof Array)){
14108                     return rs[selector];
14109                 }
14110                 for(var i = 0; i < selector.length; i++){
14111                         if(rs[selector[i]]){
14112                                 return rs[selector[i]];
14113                         }
14114                 }
14115                 return null;
14116         },
14117         
14118         
14119         /**
14120     * Updates a rule property
14121     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14122     * @param {String} property The css property
14123     * @param {String} value The new value for the property
14124     * @return {Boolean} true If a rule was found and updated
14125     */
14126    updateRule : function(selector, property, value){
14127                 if(!(selector instanceof Array)){
14128                         var rule = this.getRule(selector);
14129                         if(rule){
14130                                 rule.style[property.replace(camelRe, camelFn)] = value;
14131                                 return true;
14132                         }
14133                 }else{
14134                         for(var i = 0; i < selector.length; i++){
14135                                 if(this.updateRule(selector[i], property, value)){
14136                                         return true;
14137                                 }
14138                         }
14139                 }
14140                 return false;
14141         }
14142    };   
14143 }();/*
14144  * Based on:
14145  * Ext JS Library 1.1.1
14146  * Copyright(c) 2006-2007, Ext JS, LLC.
14147  *
14148  * Originally Released Under LGPL - original licence link has changed is not relivant.
14149  *
14150  * Fork - LGPL
14151  * <script type="text/javascript">
14152  */
14153
14154  
14155
14156 /**
14157  * @class Roo.util.ClickRepeater
14158  * @extends Roo.util.Observable
14159  * 
14160  * A wrapper class which can be applied to any element. Fires a "click" event while the
14161  * mouse is pressed. The interval between firings may be specified in the config but
14162  * defaults to 10 milliseconds.
14163  * 
14164  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14165  * 
14166  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14167  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14168  * Similar to an autorepeat key delay.
14169  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14170  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14171  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14172  *           "interval" and "delay" are ignored. "immediate" is honored.
14173  * @cfg {Boolean} preventDefault True to prevent the default click event
14174  * @cfg {Boolean} stopDefault True to stop the default click event
14175  * 
14176  * @history
14177  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14178  *     2007-02-02 jvs Renamed to ClickRepeater
14179  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14180  *
14181  *  @constructor
14182  * @param {String/HTMLElement/Element} el The element to listen on
14183  * @param {Object} config
14184  **/
14185 Roo.util.ClickRepeater = function(el, config)
14186 {
14187     this.el = Roo.get(el);
14188     this.el.unselectable();
14189
14190     Roo.apply(this, config);
14191
14192     this.addEvents({
14193     /**
14194      * @event mousedown
14195      * Fires when the mouse button is depressed.
14196      * @param {Roo.util.ClickRepeater} this
14197      */
14198         "mousedown" : true,
14199     /**
14200      * @event click
14201      * Fires on a specified interval during the time the element is pressed.
14202      * @param {Roo.util.ClickRepeater} this
14203      */
14204         "click" : true,
14205     /**
14206      * @event mouseup
14207      * Fires when the mouse key is released.
14208      * @param {Roo.util.ClickRepeater} this
14209      */
14210         "mouseup" : true
14211     });
14212
14213     this.el.on("mousedown", this.handleMouseDown, this);
14214     if(this.preventDefault || this.stopDefault){
14215         this.el.on("click", function(e){
14216             if(this.preventDefault){
14217                 e.preventDefault();
14218             }
14219             if(this.stopDefault){
14220                 e.stopEvent();
14221             }
14222         }, this);
14223     }
14224
14225     // allow inline handler
14226     if(this.handler){
14227         this.on("click", this.handler,  this.scope || this);
14228     }
14229
14230     Roo.util.ClickRepeater.superclass.constructor.call(this);
14231 };
14232
14233 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14234     interval : 20,
14235     delay: 250,
14236     preventDefault : true,
14237     stopDefault : false,
14238     timer : 0,
14239
14240     // private
14241     handleMouseDown : function(){
14242         clearTimeout(this.timer);
14243         this.el.blur();
14244         if(this.pressClass){
14245             this.el.addClass(this.pressClass);
14246         }
14247         this.mousedownTime = new Date();
14248
14249         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14250         this.el.on("mouseout", this.handleMouseOut, this);
14251
14252         this.fireEvent("mousedown", this);
14253         this.fireEvent("click", this);
14254         
14255         this.timer = this.click.defer(this.delay || this.interval, this);
14256     },
14257
14258     // private
14259     click : function(){
14260         this.fireEvent("click", this);
14261         this.timer = this.click.defer(this.getInterval(), this);
14262     },
14263
14264     // private
14265     getInterval: function(){
14266         if(!this.accelerate){
14267             return this.interval;
14268         }
14269         var pressTime = this.mousedownTime.getElapsed();
14270         if(pressTime < 500){
14271             return 400;
14272         }else if(pressTime < 1700){
14273             return 320;
14274         }else if(pressTime < 2600){
14275             return 250;
14276         }else if(pressTime < 3500){
14277             return 180;
14278         }else if(pressTime < 4400){
14279             return 140;
14280         }else if(pressTime < 5300){
14281             return 80;
14282         }else if(pressTime < 6200){
14283             return 50;
14284         }else{
14285             return 10;
14286         }
14287     },
14288
14289     // private
14290     handleMouseOut : function(){
14291         clearTimeout(this.timer);
14292         if(this.pressClass){
14293             this.el.removeClass(this.pressClass);
14294         }
14295         this.el.on("mouseover", this.handleMouseReturn, this);
14296     },
14297
14298     // private
14299     handleMouseReturn : function(){
14300         this.el.un("mouseover", this.handleMouseReturn);
14301         if(this.pressClass){
14302             this.el.addClass(this.pressClass);
14303         }
14304         this.click();
14305     },
14306
14307     // private
14308     handleMouseUp : function(){
14309         clearTimeout(this.timer);
14310         this.el.un("mouseover", this.handleMouseReturn);
14311         this.el.un("mouseout", this.handleMouseOut);
14312         Roo.get(document).un("mouseup", this.handleMouseUp);
14313         this.el.removeClass(this.pressClass);
14314         this.fireEvent("mouseup", this);
14315     }
14316 });/*
14317  * Based on:
14318  * Ext JS Library 1.1.1
14319  * Copyright(c) 2006-2007, Ext JS, LLC.
14320  *
14321  * Originally Released Under LGPL - original licence link has changed is not relivant.
14322  *
14323  * Fork - LGPL
14324  * <script type="text/javascript">
14325  */
14326
14327  
14328 /**
14329  * @class Roo.KeyNav
14330  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14331  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14332  * way to implement custom navigation schemes for any UI component.</p>
14333  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14334  * pageUp, pageDown, del, home, end.  Usage:</p>
14335  <pre><code>
14336 var nav = new Roo.KeyNav("my-element", {
14337     "left" : function(e){
14338         this.moveLeft(e.ctrlKey);
14339     },
14340     "right" : function(e){
14341         this.moveRight(e.ctrlKey);
14342     },
14343     "enter" : function(e){
14344         this.save();
14345     },
14346     scope : this
14347 });
14348 </code></pre>
14349  * @constructor
14350  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14351  * @param {Object} config The config
14352  */
14353 Roo.KeyNav = function(el, config){
14354     this.el = Roo.get(el);
14355     Roo.apply(this, config);
14356     if(!this.disabled){
14357         this.disabled = true;
14358         this.enable();
14359     }
14360 };
14361
14362 Roo.KeyNav.prototype = {
14363     /**
14364      * @cfg {Boolean} disabled
14365      * True to disable this KeyNav instance (defaults to false)
14366      */
14367     disabled : false,
14368     /**
14369      * @cfg {String} defaultEventAction
14370      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14371      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14372      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14373      */
14374     defaultEventAction: "stopEvent",
14375     /**
14376      * @cfg {Boolean} forceKeyDown
14377      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14378      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14379      * handle keydown instead of keypress.
14380      */
14381     forceKeyDown : false,
14382
14383     // private
14384     prepareEvent : function(e){
14385         var k = e.getKey();
14386         var h = this.keyToHandler[k];
14387         //if(h && this[h]){
14388         //    e.stopPropagation();
14389         //}
14390         if(Roo.isSafari && h && k >= 37 && k <= 40){
14391             e.stopEvent();
14392         }
14393     },
14394
14395     // private
14396     relay : function(e){
14397         var k = e.getKey();
14398         var h = this.keyToHandler[k];
14399         if(h && this[h]){
14400             if(this.doRelay(e, this[h], h) !== true){
14401                 e[this.defaultEventAction]();
14402             }
14403         }
14404     },
14405
14406     // private
14407     doRelay : function(e, h, hname){
14408         return h.call(this.scope || this, e);
14409     },
14410
14411     // possible handlers
14412     enter : false,
14413     left : false,
14414     right : false,
14415     up : false,
14416     down : false,
14417     tab : false,
14418     esc : false,
14419     pageUp : false,
14420     pageDown : false,
14421     del : false,
14422     home : false,
14423     end : false,
14424
14425     // quick lookup hash
14426     keyToHandler : {
14427         37 : "left",
14428         39 : "right",
14429         38 : "up",
14430         40 : "down",
14431         33 : "pageUp",
14432         34 : "pageDown",
14433         46 : "del",
14434         36 : "home",
14435         35 : "end",
14436         13 : "enter",
14437         27 : "esc",
14438         9  : "tab"
14439     },
14440
14441         /**
14442          * Enable this KeyNav
14443          */
14444         enable: function(){
14445                 if(this.disabled){
14446             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14447             // the EventObject will normalize Safari automatically
14448             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14449                 this.el.on("keydown", this.relay,  this);
14450             }else{
14451                 this.el.on("keydown", this.prepareEvent,  this);
14452                 this.el.on("keypress", this.relay,  this);
14453             }
14454                     this.disabled = false;
14455                 }
14456         },
14457
14458         /**
14459          * Disable this KeyNav
14460          */
14461         disable: function(){
14462                 if(!this.disabled){
14463                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14464                 this.el.un("keydown", this.relay);
14465             }else{
14466                 this.el.un("keydown", this.prepareEvent);
14467                 this.el.un("keypress", this.relay);
14468             }
14469                     this.disabled = true;
14470                 }
14471         }
14472 };/*
14473  * Based on:
14474  * Ext JS Library 1.1.1
14475  * Copyright(c) 2006-2007, Ext JS, LLC.
14476  *
14477  * Originally Released Under LGPL - original licence link has changed is not relivant.
14478  *
14479  * Fork - LGPL
14480  * <script type="text/javascript">
14481  */
14482
14483  
14484 /**
14485  * @class Roo.KeyMap
14486  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14487  * The constructor accepts the same config object as defined by {@link #addBinding}.
14488  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14489  * combination it will call the function with this signature (if the match is a multi-key
14490  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14491  * A KeyMap can also handle a string representation of keys.<br />
14492  * Usage:
14493  <pre><code>
14494 // map one key by key code
14495 var map = new Roo.KeyMap("my-element", {
14496     key: 13, // or Roo.EventObject.ENTER
14497     fn: myHandler,
14498     scope: myObject
14499 });
14500
14501 // map multiple keys to one action by string
14502 var map = new Roo.KeyMap("my-element", {
14503     key: "a\r\n\t",
14504     fn: myHandler,
14505     scope: myObject
14506 });
14507
14508 // map multiple keys to multiple actions by strings and array of codes
14509 var map = new Roo.KeyMap("my-element", [
14510     {
14511         key: [10,13],
14512         fn: function(){ alert("Return was pressed"); }
14513     }, {
14514         key: "abc",
14515         fn: function(){ alert('a, b or c was pressed'); }
14516     }, {
14517         key: "\t",
14518         ctrl:true,
14519         shift:true,
14520         fn: function(){ alert('Control + shift + tab was pressed.'); }
14521     }
14522 ]);
14523 </code></pre>
14524  * <b>Note: A KeyMap starts enabled</b>
14525  * @constructor
14526  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14527  * @param {Object} config The config (see {@link #addBinding})
14528  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14529  */
14530 Roo.KeyMap = function(el, config, eventName){
14531     this.el  = Roo.get(el);
14532     this.eventName = eventName || "keydown";
14533     this.bindings = [];
14534     if(config){
14535         this.addBinding(config);
14536     }
14537     this.enable();
14538 };
14539
14540 Roo.KeyMap.prototype = {
14541     /**
14542      * True to stop the event from bubbling and prevent the default browser action if the
14543      * key was handled by the KeyMap (defaults to false)
14544      * @type Boolean
14545      */
14546     stopEvent : false,
14547
14548     /**
14549      * Add a new binding to this KeyMap. The following config object properties are supported:
14550      * <pre>
14551 Property    Type             Description
14552 ----------  ---------------  ----------------------------------------------------------------------
14553 key         String/Array     A single keycode or an array of keycodes to handle
14554 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14555 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14556 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14557 fn          Function         The function to call when KeyMap finds the expected key combination
14558 scope       Object           The scope of the callback function
14559 </pre>
14560      *
14561      * Usage:
14562      * <pre><code>
14563 // Create a KeyMap
14564 var map = new Roo.KeyMap(document, {
14565     key: Roo.EventObject.ENTER,
14566     fn: handleKey,
14567     scope: this
14568 });
14569
14570 //Add a new binding to the existing KeyMap later
14571 map.addBinding({
14572     key: 'abc',
14573     shift: true,
14574     fn: handleKey,
14575     scope: this
14576 });
14577 </code></pre>
14578      * @param {Object/Array} config A single KeyMap config or an array of configs
14579      */
14580         addBinding : function(config){
14581         if(config instanceof Array){
14582             for(var i = 0, len = config.length; i < len; i++){
14583                 this.addBinding(config[i]);
14584             }
14585             return;
14586         }
14587         var keyCode = config.key,
14588             shift = config.shift, 
14589             ctrl = config.ctrl, 
14590             alt = config.alt,
14591             fn = config.fn,
14592             scope = config.scope;
14593         if(typeof keyCode == "string"){
14594             var ks = [];
14595             var keyString = keyCode.toUpperCase();
14596             for(var j = 0, len = keyString.length; j < len; j++){
14597                 ks.push(keyString.charCodeAt(j));
14598             }
14599             keyCode = ks;
14600         }
14601         var keyArray = keyCode instanceof Array;
14602         var handler = function(e){
14603             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14604                 var k = e.getKey();
14605                 if(keyArray){
14606                     for(var i = 0, len = keyCode.length; i < len; i++){
14607                         if(keyCode[i] == k){
14608                           if(this.stopEvent){
14609                               e.stopEvent();
14610                           }
14611                           fn.call(scope || window, k, e);
14612                           return;
14613                         }
14614                     }
14615                 }else{
14616                     if(k == keyCode){
14617                         if(this.stopEvent){
14618                            e.stopEvent();
14619                         }
14620                         fn.call(scope || window, k, e);
14621                     }
14622                 }
14623             }
14624         };
14625         this.bindings.push(handler);  
14626         },
14627
14628     /**
14629      * Shorthand for adding a single key listener
14630      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14631      * following options:
14632      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14633      * @param {Function} fn The function to call
14634      * @param {Object} scope (optional) The scope of the function
14635      */
14636     on : function(key, fn, scope){
14637         var keyCode, shift, ctrl, alt;
14638         if(typeof key == "object" && !(key instanceof Array)){
14639             keyCode = key.key;
14640             shift = key.shift;
14641             ctrl = key.ctrl;
14642             alt = key.alt;
14643         }else{
14644             keyCode = key;
14645         }
14646         this.addBinding({
14647             key: keyCode,
14648             shift: shift,
14649             ctrl: ctrl,
14650             alt: alt,
14651             fn: fn,
14652             scope: scope
14653         })
14654     },
14655
14656     // private
14657     handleKeyDown : function(e){
14658             if(this.enabled){ //just in case
14659             var b = this.bindings;
14660             for(var i = 0, len = b.length; i < len; i++){
14661                 b[i].call(this, e);
14662             }
14663             }
14664         },
14665         
14666         /**
14667          * Returns true if this KeyMap is enabled
14668          * @return {Boolean} 
14669          */
14670         isEnabled : function(){
14671             return this.enabled;  
14672         },
14673         
14674         /**
14675          * Enables this KeyMap
14676          */
14677         enable: function(){
14678                 if(!this.enabled){
14679                     this.el.on(this.eventName, this.handleKeyDown, this);
14680                     this.enabled = true;
14681                 }
14682         },
14683
14684         /**
14685          * Disable this KeyMap
14686          */
14687         disable: function(){
14688                 if(this.enabled){
14689                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14690                     this.enabled = false;
14691                 }
14692         }
14693 };/*
14694  * Based on:
14695  * Ext JS Library 1.1.1
14696  * Copyright(c) 2006-2007, Ext JS, LLC.
14697  *
14698  * Originally Released Under LGPL - original licence link has changed is not relivant.
14699  *
14700  * Fork - LGPL
14701  * <script type="text/javascript">
14702  */
14703
14704  
14705 /**
14706  * @class Roo.util.TextMetrics
14707  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14708  * wide, in pixels, a given block of text will be.
14709  * @singleton
14710  */
14711 Roo.util.TextMetrics = function(){
14712     var shared;
14713     return {
14714         /**
14715          * Measures the size of the specified text
14716          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14717          * that can affect the size of the rendered text
14718          * @param {String} text The text to measure
14719          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14720          * in order to accurately measure the text height
14721          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14722          */
14723         measure : function(el, text, fixedWidth){
14724             if(!shared){
14725                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14726             }
14727             shared.bind(el);
14728             shared.setFixedWidth(fixedWidth || 'auto');
14729             return shared.getSize(text);
14730         },
14731
14732         /**
14733          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14734          * the overhead of multiple calls to initialize the style properties on each measurement.
14735          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14736          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14737          * in order to accurately measure the text height
14738          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14739          */
14740         createInstance : function(el, fixedWidth){
14741             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14742         }
14743     };
14744 }();
14745
14746  
14747
14748 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14749     var ml = new Roo.Element(document.createElement('div'));
14750     document.body.appendChild(ml.dom);
14751     ml.position('absolute');
14752     ml.setLeftTop(-1000, -1000);
14753     ml.hide();
14754
14755     if(fixedWidth){
14756         ml.setWidth(fixedWidth);
14757     }
14758      
14759     var instance = {
14760         /**
14761          * Returns the size of the specified text based on the internal element's style and width properties
14762          * @memberOf Roo.util.TextMetrics.Instance#
14763          * @param {String} text The text to measure
14764          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14765          */
14766         getSize : function(text){
14767             ml.update(text);
14768             var s = ml.getSize();
14769             ml.update('');
14770             return s;
14771         },
14772
14773         /**
14774          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14775          * that can affect the size of the rendered text
14776          * @memberOf Roo.util.TextMetrics.Instance#
14777          * @param {String/HTMLElement} el The element, dom node or id
14778          */
14779         bind : function(el){
14780             ml.setStyle(
14781                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14782             );
14783         },
14784
14785         /**
14786          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14787          * to set a fixed width in order to accurately measure the text height.
14788          * @memberOf Roo.util.TextMetrics.Instance#
14789          * @param {Number} width The width to set on the element
14790          */
14791         setFixedWidth : function(width){
14792             ml.setWidth(width);
14793         },
14794
14795         /**
14796          * Returns the measured width of the specified text
14797          * @memberOf Roo.util.TextMetrics.Instance#
14798          * @param {String} text The text to measure
14799          * @return {Number} width The width in pixels
14800          */
14801         getWidth : function(text){
14802             ml.dom.style.width = 'auto';
14803             return this.getSize(text).width;
14804         },
14805
14806         /**
14807          * Returns the measured height of the specified text.  For multiline text, be sure to call
14808          * {@link #setFixedWidth} if necessary.
14809          * @memberOf Roo.util.TextMetrics.Instance#
14810          * @param {String} text The text to measure
14811          * @return {Number} height The height in pixels
14812          */
14813         getHeight : function(text){
14814             return this.getSize(text).height;
14815         }
14816     };
14817
14818     instance.bind(bindTo);
14819
14820     return instance;
14821 };
14822
14823 // backwards compat
14824 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14825  * Based on:
14826  * Ext JS Library 1.1.1
14827  * Copyright(c) 2006-2007, Ext JS, LLC.
14828  *
14829  * Originally Released Under LGPL - original licence link has changed is not relivant.
14830  *
14831  * Fork - LGPL
14832  * <script type="text/javascript">
14833  */
14834
14835 /**
14836  * @class Roo.state.Provider
14837  * Abstract base class for state provider implementations. This class provides methods
14838  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14839  * Provider interface.
14840  */
14841 Roo.state.Provider = function(){
14842     /**
14843      * @event statechange
14844      * Fires when a state change occurs.
14845      * @param {Provider} this This state provider
14846      * @param {String} key The state key which was changed
14847      * @param {String} value The encoded value for the state
14848      */
14849     this.addEvents({
14850         "statechange": true
14851     });
14852     this.state = {};
14853     Roo.state.Provider.superclass.constructor.call(this);
14854 };
14855 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14856     /**
14857      * Returns the current value for a key
14858      * @param {String} name The key name
14859      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14860      * @return {Mixed} The state data
14861      */
14862     get : function(name, defaultValue){
14863         return typeof this.state[name] == "undefined" ?
14864             defaultValue : this.state[name];
14865     },
14866     
14867     /**
14868      * Clears a value from the state
14869      * @param {String} name The key name
14870      */
14871     clear : function(name){
14872         delete this.state[name];
14873         this.fireEvent("statechange", this, name, null);
14874     },
14875     
14876     /**
14877      * Sets the value for a key
14878      * @param {String} name The key name
14879      * @param {Mixed} value The value to set
14880      */
14881     set : function(name, value){
14882         this.state[name] = value;
14883         this.fireEvent("statechange", this, name, value);
14884     },
14885     
14886     /**
14887      * Decodes a string previously encoded with {@link #encodeValue}.
14888      * @param {String} value The value to decode
14889      * @return {Mixed} The decoded value
14890      */
14891     decodeValue : function(cookie){
14892         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14893         var matches = re.exec(unescape(cookie));
14894         if(!matches || !matches[1]) {
14895             return; // non state cookie
14896         }
14897         var type = matches[1];
14898         var v = matches[2];
14899         switch(type){
14900             case "n":
14901                 return parseFloat(v);
14902             case "d":
14903                 return new Date(Date.parse(v));
14904             case "b":
14905                 return (v == "1");
14906             case "a":
14907                 var all = [];
14908                 var values = v.split("^");
14909                 for(var i = 0, len = values.length; i < len; i++){
14910                     all.push(this.decodeValue(values[i]));
14911                 }
14912                 return all;
14913            case "o":
14914                 var all = {};
14915                 var values = v.split("^");
14916                 for(var i = 0, len = values.length; i < len; i++){
14917                     var kv = values[i].split("=");
14918                     all[kv[0]] = this.decodeValue(kv[1]);
14919                 }
14920                 return all;
14921            default:
14922                 return v;
14923         }
14924     },
14925     
14926     /**
14927      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14928      * @param {Mixed} value The value to encode
14929      * @return {String} The encoded value
14930      */
14931     encodeValue : function(v){
14932         var enc;
14933         if(typeof v == "number"){
14934             enc = "n:" + v;
14935         }else if(typeof v == "boolean"){
14936             enc = "b:" + (v ? "1" : "0");
14937         }else if(v instanceof Date){
14938             enc = "d:" + v.toGMTString();
14939         }else if(v instanceof Array){
14940             var flat = "";
14941             for(var i = 0, len = v.length; i < len; i++){
14942                 flat += this.encodeValue(v[i]);
14943                 if(i != len-1) {
14944                     flat += "^";
14945                 }
14946             }
14947             enc = "a:" + flat;
14948         }else if(typeof v == "object"){
14949             var flat = "";
14950             for(var key in v){
14951                 if(typeof v[key] != "function"){
14952                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14953                 }
14954             }
14955             enc = "o:" + flat.substring(0, flat.length-1);
14956         }else{
14957             enc = "s:" + v;
14958         }
14959         return escape(enc);        
14960     }
14961 });
14962
14963 /*
14964  * Based on:
14965  * Ext JS Library 1.1.1
14966  * Copyright(c) 2006-2007, Ext JS, LLC.
14967  *
14968  * Originally Released Under LGPL - original licence link has changed is not relivant.
14969  *
14970  * Fork - LGPL
14971  * <script type="text/javascript">
14972  */
14973 /**
14974  * @class Roo.state.Manager
14975  * This is the global state manager. By default all components that are "state aware" check this class
14976  * for state information if you don't pass them a custom state provider. In order for this class
14977  * to be useful, it must be initialized with a provider when your application initializes.
14978  <pre><code>
14979 // in your initialization function
14980 init : function(){
14981    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14982    ...
14983    // supposed you have a {@link Roo.BorderLayout}
14984    var layout = new Roo.BorderLayout(...);
14985    layout.restoreState();
14986    // or a {Roo.BasicDialog}
14987    var dialog = new Roo.BasicDialog(...);
14988    dialog.restoreState();
14989  </code></pre>
14990  * @singleton
14991  */
14992 Roo.state.Manager = function(){
14993     var provider = new Roo.state.Provider();
14994     
14995     return {
14996         /**
14997          * Configures the default state provider for your application
14998          * @param {Provider} stateProvider The state provider to set
14999          */
15000         setProvider : function(stateProvider){
15001             provider = stateProvider;
15002         },
15003         
15004         /**
15005          * Returns the current value for a key
15006          * @param {String} name The key name
15007          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15008          * @return {Mixed} The state data
15009          */
15010         get : function(key, defaultValue){
15011             return provider.get(key, defaultValue);
15012         },
15013         
15014         /**
15015          * Sets the value for a key
15016          * @param {String} name The key name
15017          * @param {Mixed} value The state data
15018          */
15019          set : function(key, value){
15020             provider.set(key, value);
15021         },
15022         
15023         /**
15024          * Clears a value from the state
15025          * @param {String} name The key name
15026          */
15027         clear : function(key){
15028             provider.clear(key);
15029         },
15030         
15031         /**
15032          * Gets the currently configured state provider
15033          * @return {Provider} The state provider
15034          */
15035         getProvider : function(){
15036             return provider;
15037         }
15038     };
15039 }();
15040 /*
15041  * Based on:
15042  * Ext JS Library 1.1.1
15043  * Copyright(c) 2006-2007, Ext JS, LLC.
15044  *
15045  * Originally Released Under LGPL - original licence link has changed is not relivant.
15046  *
15047  * Fork - LGPL
15048  * <script type="text/javascript">
15049  */
15050 /**
15051  * @class Roo.state.CookieProvider
15052  * @extends Roo.state.Provider
15053  * The default Provider implementation which saves state via cookies.
15054  * <br />Usage:
15055  <pre><code>
15056    var cp = new Roo.state.CookieProvider({
15057        path: "/cgi-bin/",
15058        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15059        domain: "roojs.com"
15060    })
15061    Roo.state.Manager.setProvider(cp);
15062  </code></pre>
15063  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15064  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15065  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15066  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15067  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15068  * domain the page is running on including the 'www' like 'www.roojs.com')
15069  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15070  * @constructor
15071  * Create a new CookieProvider
15072  * @param {Object} config The configuration object
15073  */
15074 Roo.state.CookieProvider = function(config){
15075     Roo.state.CookieProvider.superclass.constructor.call(this);
15076     this.path = "/";
15077     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15078     this.domain = null;
15079     this.secure = false;
15080     Roo.apply(this, config);
15081     this.state = this.readCookies();
15082 };
15083
15084 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15085     // private
15086     set : function(name, value){
15087         if(typeof value == "undefined" || value === null){
15088             this.clear(name);
15089             return;
15090         }
15091         this.setCookie(name, value);
15092         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15093     },
15094
15095     // private
15096     clear : function(name){
15097         this.clearCookie(name);
15098         Roo.state.CookieProvider.superclass.clear.call(this, name);
15099     },
15100
15101     // private
15102     readCookies : function(){
15103         var cookies = {};
15104         var c = document.cookie + ";";
15105         var re = /\s?(.*?)=(.*?);/g;
15106         var matches;
15107         while((matches = re.exec(c)) != null){
15108             var name = matches[1];
15109             var value = matches[2];
15110             if(name && name.substring(0,3) == "ys-"){
15111                 cookies[name.substr(3)] = this.decodeValue(value);
15112             }
15113         }
15114         return cookies;
15115     },
15116
15117     // private
15118     setCookie : function(name, value){
15119         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15120            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15121            ((this.path == null) ? "" : ("; path=" + this.path)) +
15122            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15123            ((this.secure == true) ? "; secure" : "");
15124     },
15125
15126     // private
15127     clearCookie : function(name){
15128         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15129            ((this.path == null) ? "" : ("; path=" + this.path)) +
15130            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15131            ((this.secure == true) ? "; secure" : "");
15132     }
15133 });/*
15134  * Based on:
15135  * Ext JS Library 1.1.1
15136  * Copyright(c) 2006-2007, Ext JS, LLC.
15137  *
15138  * Originally Released Under LGPL - original licence link has changed is not relivant.
15139  *
15140  * Fork - LGPL
15141  * <script type="text/javascript">
15142  */
15143  
15144
15145 /**
15146  * @class Roo.ComponentMgr
15147  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15148  * @singleton
15149  */
15150 Roo.ComponentMgr = function(){
15151     var all = new Roo.util.MixedCollection();
15152
15153     return {
15154         /**
15155          * Registers a component.
15156          * @param {Roo.Component} c The component
15157          */
15158         register : function(c){
15159             all.add(c);
15160         },
15161
15162         /**
15163          * Unregisters a component.
15164          * @param {Roo.Component} c The component
15165          */
15166         unregister : function(c){
15167             all.remove(c);
15168         },
15169
15170         /**
15171          * Returns a component by id
15172          * @param {String} id The component id
15173          */
15174         get : function(id){
15175             return all.get(id);
15176         },
15177
15178         /**
15179          * Registers a function that will be called when a specified component is added to ComponentMgr
15180          * @param {String} id The component id
15181          * @param {Funtction} fn The callback function
15182          * @param {Object} scope The scope of the callback
15183          */
15184         onAvailable : function(id, fn, scope){
15185             all.on("add", function(index, o){
15186                 if(o.id == id){
15187                     fn.call(scope || o, o);
15188                     all.un("add", fn, scope);
15189                 }
15190             });
15191         }
15192     };
15193 }();/*
15194  * Based on:
15195  * Ext JS Library 1.1.1
15196  * Copyright(c) 2006-2007, Ext JS, LLC.
15197  *
15198  * Originally Released Under LGPL - original licence link has changed is not relivant.
15199  *
15200  * Fork - LGPL
15201  * <script type="text/javascript">
15202  */
15203  
15204 /**
15205  * @class Roo.Component
15206  * @extends Roo.util.Observable
15207  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15208  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15209  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15210  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15211  * All visual components (widgets) that require rendering into a layout should subclass Component.
15212  * @constructor
15213  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15214  * 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
15215  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15216  */
15217 Roo.Component = function(config){
15218     config = config || {};
15219     if(config.tagName || config.dom || typeof config == "string"){ // element object
15220         config = {el: config, id: config.id || config};
15221     }
15222     this.initialConfig = config;
15223
15224     Roo.apply(this, config);
15225     this.addEvents({
15226         /**
15227          * @event disable
15228          * Fires after the component is disabled.
15229              * @param {Roo.Component} this
15230              */
15231         disable : true,
15232         /**
15233          * @event enable
15234          * Fires after the component is enabled.
15235              * @param {Roo.Component} this
15236              */
15237         enable : true,
15238         /**
15239          * @event beforeshow
15240          * Fires before the component is shown.  Return false to stop the show.
15241              * @param {Roo.Component} this
15242              */
15243         beforeshow : true,
15244         /**
15245          * @event show
15246          * Fires after the component is shown.
15247              * @param {Roo.Component} this
15248              */
15249         show : true,
15250         /**
15251          * @event beforehide
15252          * Fires before the component is hidden. Return false to stop the hide.
15253              * @param {Roo.Component} this
15254              */
15255         beforehide : true,
15256         /**
15257          * @event hide
15258          * Fires after the component is hidden.
15259              * @param {Roo.Component} this
15260              */
15261         hide : true,
15262         /**
15263          * @event beforerender
15264          * Fires before the component is rendered. Return false to stop the render.
15265              * @param {Roo.Component} this
15266              */
15267         beforerender : true,
15268         /**
15269          * @event render
15270          * Fires after the component is rendered.
15271              * @param {Roo.Component} this
15272              */
15273         render : true,
15274         /**
15275          * @event beforedestroy
15276          * Fires before the component is destroyed. Return false to stop the destroy.
15277              * @param {Roo.Component} this
15278              */
15279         beforedestroy : true,
15280         /**
15281          * @event destroy
15282          * Fires after the component is destroyed.
15283              * @param {Roo.Component} this
15284              */
15285         destroy : true
15286     });
15287     if(!this.id){
15288         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15289     }
15290     Roo.ComponentMgr.register(this);
15291     Roo.Component.superclass.constructor.call(this);
15292     this.initComponent();
15293     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15294         this.render(this.renderTo);
15295         delete this.renderTo;
15296     }
15297 };
15298
15299 /** @private */
15300 Roo.Component.AUTO_ID = 1000;
15301
15302 Roo.extend(Roo.Component, Roo.util.Observable, {
15303     /**
15304      * @scope Roo.Component.prototype
15305      * @type {Boolean}
15306      * true if this component is hidden. Read-only.
15307      */
15308     hidden : false,
15309     /**
15310      * @type {Boolean}
15311      * true if this component is disabled. Read-only.
15312      */
15313     disabled : false,
15314     /**
15315      * @type {Boolean}
15316      * true if this component has been rendered. Read-only.
15317      */
15318     rendered : false,
15319     
15320     /** @cfg {String} disableClass
15321      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15322      */
15323     disabledClass : "x-item-disabled",
15324         /** @cfg {Boolean} allowDomMove
15325          * Whether the component can move the Dom node when rendering (defaults to true).
15326          */
15327     allowDomMove : true,
15328     /** @cfg {String} hideMode (display|visibility)
15329      * How this component should hidden. Supported values are
15330      * "visibility" (css visibility), "offsets" (negative offset position) and
15331      * "display" (css display) - defaults to "display".
15332      */
15333     hideMode: 'display',
15334
15335     /** @private */
15336     ctype : "Roo.Component",
15337
15338     /**
15339      * @cfg {String} actionMode 
15340      * which property holds the element that used for  hide() / show() / disable() / enable()
15341      * default is 'el' 
15342      */
15343     actionMode : "el",
15344
15345     /** @private */
15346     getActionEl : function(){
15347         return this[this.actionMode];
15348     },
15349
15350     initComponent : Roo.emptyFn,
15351     /**
15352      * If this is a lazy rendering component, render it to its container element.
15353      * @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.
15354      */
15355     render : function(container, position){
15356         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15357             if(!container && this.el){
15358                 this.el = Roo.get(this.el);
15359                 container = this.el.dom.parentNode;
15360                 this.allowDomMove = false;
15361             }
15362             this.container = Roo.get(container);
15363             this.rendered = true;
15364             if(position !== undefined){
15365                 if(typeof position == 'number'){
15366                     position = this.container.dom.childNodes[position];
15367                 }else{
15368                     position = Roo.getDom(position);
15369                 }
15370             }
15371             this.onRender(this.container, position || null);
15372             if(this.cls){
15373                 this.el.addClass(this.cls);
15374                 delete this.cls;
15375             }
15376             if(this.style){
15377                 this.el.applyStyles(this.style);
15378                 delete this.style;
15379             }
15380             this.fireEvent("render", this);
15381             this.afterRender(this.container);
15382             if(this.hidden){
15383                 this.hide();
15384             }
15385             if(this.disabled){
15386                 this.disable();
15387             }
15388         }
15389         return this;
15390     },
15391
15392     /** @private */
15393     // default function is not really useful
15394     onRender : function(ct, position){
15395         if(this.el){
15396             this.el = Roo.get(this.el);
15397             if(this.allowDomMove !== false){
15398                 ct.dom.insertBefore(this.el.dom, position);
15399             }
15400         }
15401     },
15402
15403     /** @private */
15404     getAutoCreate : function(){
15405         var cfg = typeof this.autoCreate == "object" ?
15406                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15407         if(this.id && !cfg.id){
15408             cfg.id = this.id;
15409         }
15410         return cfg;
15411     },
15412
15413     /** @private */
15414     afterRender : Roo.emptyFn,
15415
15416     /**
15417      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15418      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15419      */
15420     destroy : function(){
15421         if(this.fireEvent("beforedestroy", this) !== false){
15422             this.purgeListeners();
15423             this.beforeDestroy();
15424             if(this.rendered){
15425                 this.el.removeAllListeners();
15426                 this.el.remove();
15427                 if(this.actionMode == "container"){
15428                     this.container.remove();
15429                 }
15430             }
15431             this.onDestroy();
15432             Roo.ComponentMgr.unregister(this);
15433             this.fireEvent("destroy", this);
15434         }
15435     },
15436
15437         /** @private */
15438     beforeDestroy : function(){
15439
15440     },
15441
15442         /** @private */
15443         onDestroy : function(){
15444
15445     },
15446
15447     /**
15448      * Returns the underlying {@link Roo.Element}.
15449      * @return {Roo.Element} The element
15450      */
15451     getEl : function(){
15452         return this.el;
15453     },
15454
15455     /**
15456      * Returns the id of this component.
15457      * @return {String}
15458      */
15459     getId : function(){
15460         return this.id;
15461     },
15462
15463     /**
15464      * Try to focus this component.
15465      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15466      * @return {Roo.Component} this
15467      */
15468     focus : function(selectText){
15469         if(this.rendered){
15470             this.el.focus();
15471             if(selectText === true){
15472                 this.el.dom.select();
15473             }
15474         }
15475         return this;
15476     },
15477
15478     /** @private */
15479     blur : function(){
15480         if(this.rendered){
15481             this.el.blur();
15482         }
15483         return this;
15484     },
15485
15486     /**
15487      * Disable this component.
15488      * @return {Roo.Component} this
15489      */
15490     disable : function(){
15491         if(this.rendered){
15492             this.onDisable();
15493         }
15494         this.disabled = true;
15495         this.fireEvent("disable", this);
15496         return this;
15497     },
15498
15499         // private
15500     onDisable : function(){
15501         this.getActionEl().addClass(this.disabledClass);
15502         this.el.dom.disabled = true;
15503     },
15504
15505     /**
15506      * Enable this component.
15507      * @return {Roo.Component} this
15508      */
15509     enable : function(){
15510         if(this.rendered){
15511             this.onEnable();
15512         }
15513         this.disabled = false;
15514         this.fireEvent("enable", this);
15515         return this;
15516     },
15517
15518         // private
15519     onEnable : function(){
15520         this.getActionEl().removeClass(this.disabledClass);
15521         this.el.dom.disabled = false;
15522     },
15523
15524     /**
15525      * Convenience function for setting disabled/enabled by boolean.
15526      * @param {Boolean} disabled
15527      */
15528     setDisabled : function(disabled){
15529         this[disabled ? "disable" : "enable"]();
15530     },
15531
15532     /**
15533      * Show this component.
15534      * @return {Roo.Component} this
15535      */
15536     show: function(){
15537         if(this.fireEvent("beforeshow", this) !== false){
15538             this.hidden = false;
15539             if(this.rendered){
15540                 this.onShow();
15541             }
15542             this.fireEvent("show", this);
15543         }
15544         return this;
15545     },
15546
15547     // private
15548     onShow : function(){
15549         var ae = this.getActionEl();
15550         if(this.hideMode == 'visibility'){
15551             ae.dom.style.visibility = "visible";
15552         }else if(this.hideMode == 'offsets'){
15553             ae.removeClass('x-hidden');
15554         }else{
15555             ae.dom.style.display = "";
15556         }
15557     },
15558
15559     /**
15560      * Hide this component.
15561      * @return {Roo.Component} this
15562      */
15563     hide: function(){
15564         if(this.fireEvent("beforehide", this) !== false){
15565             this.hidden = true;
15566             if(this.rendered){
15567                 this.onHide();
15568             }
15569             this.fireEvent("hide", this);
15570         }
15571         return this;
15572     },
15573
15574     // private
15575     onHide : function(){
15576         var ae = this.getActionEl();
15577         if(this.hideMode == 'visibility'){
15578             ae.dom.style.visibility = "hidden";
15579         }else if(this.hideMode == 'offsets'){
15580             ae.addClass('x-hidden');
15581         }else{
15582             ae.dom.style.display = "none";
15583         }
15584     },
15585
15586     /**
15587      * Convenience function to hide or show this component by boolean.
15588      * @param {Boolean} visible True to show, false to hide
15589      * @return {Roo.Component} this
15590      */
15591     setVisible: function(visible){
15592         if(visible) {
15593             this.show();
15594         }else{
15595             this.hide();
15596         }
15597         return this;
15598     },
15599
15600     /**
15601      * Returns true if this component is visible.
15602      */
15603     isVisible : function(){
15604         return this.getActionEl().isVisible();
15605     },
15606
15607     cloneConfig : function(overrides){
15608         overrides = overrides || {};
15609         var id = overrides.id || Roo.id();
15610         var cfg = Roo.applyIf(overrides, this.initialConfig);
15611         cfg.id = id; // prevent dup id
15612         return new this.constructor(cfg);
15613     }
15614 });/*
15615  * Based on:
15616  * Ext JS Library 1.1.1
15617  * Copyright(c) 2006-2007, Ext JS, LLC.
15618  *
15619  * Originally Released Under LGPL - original licence link has changed is not relivant.
15620  *
15621  * Fork - LGPL
15622  * <script type="text/javascript">
15623  */
15624
15625 /**
15626  * @class Roo.BoxComponent
15627  * @extends Roo.Component
15628  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15629  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15630  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15631  * layout containers.
15632  * @constructor
15633  * @param {Roo.Element/String/Object} config The configuration options.
15634  */
15635 Roo.BoxComponent = function(config){
15636     Roo.Component.call(this, config);
15637     this.addEvents({
15638         /**
15639          * @event resize
15640          * Fires after the component is resized.
15641              * @param {Roo.Component} this
15642              * @param {Number} adjWidth The box-adjusted width that was set
15643              * @param {Number} adjHeight The box-adjusted height that was set
15644              * @param {Number} rawWidth The width that was originally specified
15645              * @param {Number} rawHeight The height that was originally specified
15646              */
15647         resize : true,
15648         /**
15649          * @event move
15650          * Fires after the component is moved.
15651              * @param {Roo.Component} this
15652              * @param {Number} x The new x position
15653              * @param {Number} y The new y position
15654              */
15655         move : true
15656     });
15657 };
15658
15659 Roo.extend(Roo.BoxComponent, Roo.Component, {
15660     // private, set in afterRender to signify that the component has been rendered
15661     boxReady : false,
15662     // private, used to defer height settings to subclasses
15663     deferHeight: false,
15664     /** @cfg {Number} width
15665      * width (optional) size of component
15666      */
15667      /** @cfg {Number} height
15668      * height (optional) size of component
15669      */
15670      
15671     /**
15672      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15673      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15674      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15675      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15676      * @return {Roo.BoxComponent} this
15677      */
15678     setSize : function(w, h){
15679         // support for standard size objects
15680         if(typeof w == 'object'){
15681             h = w.height;
15682             w = w.width;
15683         }
15684         // not rendered
15685         if(!this.boxReady){
15686             this.width = w;
15687             this.height = h;
15688             return this;
15689         }
15690
15691         // prevent recalcs when not needed
15692         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15693             return this;
15694         }
15695         this.lastSize = {width: w, height: h};
15696
15697         var adj = this.adjustSize(w, h);
15698         var aw = adj.width, ah = adj.height;
15699         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15700             var rz = this.getResizeEl();
15701             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15702                 rz.setSize(aw, ah);
15703             }else if(!this.deferHeight && ah !== undefined){
15704                 rz.setHeight(ah);
15705             }else if(aw !== undefined){
15706                 rz.setWidth(aw);
15707             }
15708             this.onResize(aw, ah, w, h);
15709             this.fireEvent('resize', this, aw, ah, w, h);
15710         }
15711         return this;
15712     },
15713
15714     /**
15715      * Gets the current size of the component's underlying element.
15716      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15717      */
15718     getSize : function(){
15719         return this.el.getSize();
15720     },
15721
15722     /**
15723      * Gets the current XY position of the component's underlying element.
15724      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15725      * @return {Array} The XY position of the element (e.g., [100, 200])
15726      */
15727     getPosition : function(local){
15728         if(local === true){
15729             return [this.el.getLeft(true), this.el.getTop(true)];
15730         }
15731         return this.xy || this.el.getXY();
15732     },
15733
15734     /**
15735      * Gets the current box measurements of the component's underlying element.
15736      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15737      * @returns {Object} box An object in the format {x, y, width, height}
15738      */
15739     getBox : function(local){
15740         var s = this.el.getSize();
15741         if(local){
15742             s.x = this.el.getLeft(true);
15743             s.y = this.el.getTop(true);
15744         }else{
15745             var xy = this.xy || this.el.getXY();
15746             s.x = xy[0];
15747             s.y = xy[1];
15748         }
15749         return s;
15750     },
15751
15752     /**
15753      * Sets the current box measurements of the component's underlying element.
15754      * @param {Object} box An object in the format {x, y, width, height}
15755      * @returns {Roo.BoxComponent} this
15756      */
15757     updateBox : function(box){
15758         this.setSize(box.width, box.height);
15759         this.setPagePosition(box.x, box.y);
15760         return this;
15761     },
15762
15763     // protected
15764     getResizeEl : function(){
15765         return this.resizeEl || this.el;
15766     },
15767
15768     // protected
15769     getPositionEl : function(){
15770         return this.positionEl || this.el;
15771     },
15772
15773     /**
15774      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15775      * This method fires the move event.
15776      * @param {Number} left The new left
15777      * @param {Number} top The new top
15778      * @returns {Roo.BoxComponent} this
15779      */
15780     setPosition : function(x, y){
15781         this.x = x;
15782         this.y = y;
15783         if(!this.boxReady){
15784             return this;
15785         }
15786         var adj = this.adjustPosition(x, y);
15787         var ax = adj.x, ay = adj.y;
15788
15789         var el = this.getPositionEl();
15790         if(ax !== undefined || ay !== undefined){
15791             if(ax !== undefined && ay !== undefined){
15792                 el.setLeftTop(ax, ay);
15793             }else if(ax !== undefined){
15794                 el.setLeft(ax);
15795             }else if(ay !== undefined){
15796                 el.setTop(ay);
15797             }
15798             this.onPosition(ax, ay);
15799             this.fireEvent('move', this, ax, ay);
15800         }
15801         return this;
15802     },
15803
15804     /**
15805      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15806      * This method fires the move event.
15807      * @param {Number} x The new x position
15808      * @param {Number} y The new y position
15809      * @returns {Roo.BoxComponent} this
15810      */
15811     setPagePosition : function(x, y){
15812         this.pageX = x;
15813         this.pageY = y;
15814         if(!this.boxReady){
15815             return;
15816         }
15817         if(x === undefined || y === undefined){ // cannot translate undefined points
15818             return;
15819         }
15820         var p = this.el.translatePoints(x, y);
15821         this.setPosition(p.left, p.top);
15822         return this;
15823     },
15824
15825     // private
15826     onRender : function(ct, position){
15827         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15828         if(this.resizeEl){
15829             this.resizeEl = Roo.get(this.resizeEl);
15830         }
15831         if(this.positionEl){
15832             this.positionEl = Roo.get(this.positionEl);
15833         }
15834     },
15835
15836     // private
15837     afterRender : function(){
15838         Roo.BoxComponent.superclass.afterRender.call(this);
15839         this.boxReady = true;
15840         this.setSize(this.width, this.height);
15841         if(this.x || this.y){
15842             this.setPosition(this.x, this.y);
15843         }
15844         if(this.pageX || this.pageY){
15845             this.setPagePosition(this.pageX, this.pageY);
15846         }
15847     },
15848
15849     /**
15850      * Force the component's size to recalculate based on the underlying element's current height and width.
15851      * @returns {Roo.BoxComponent} this
15852      */
15853     syncSize : function(){
15854         delete this.lastSize;
15855         this.setSize(this.el.getWidth(), this.el.getHeight());
15856         return this;
15857     },
15858
15859     /**
15860      * Called after the component is resized, this method is empty by default but can be implemented by any
15861      * subclass that needs to perform custom logic after a resize occurs.
15862      * @param {Number} adjWidth The box-adjusted width that was set
15863      * @param {Number} adjHeight The box-adjusted height that was set
15864      * @param {Number} rawWidth The width that was originally specified
15865      * @param {Number} rawHeight The height that was originally specified
15866      */
15867     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15868
15869     },
15870
15871     /**
15872      * Called after the component is moved, this method is empty by default but can be implemented by any
15873      * subclass that needs to perform custom logic after a move occurs.
15874      * @param {Number} x The new x position
15875      * @param {Number} y The new y position
15876      */
15877     onPosition : function(x, y){
15878
15879     },
15880
15881     // private
15882     adjustSize : function(w, h){
15883         if(this.autoWidth){
15884             w = 'auto';
15885         }
15886         if(this.autoHeight){
15887             h = 'auto';
15888         }
15889         return {width : w, height: h};
15890     },
15891
15892     // private
15893     adjustPosition : function(x, y){
15894         return {x : x, y: y};
15895     }
15896 });/*
15897  * Original code for Roojs - LGPL
15898  * <script type="text/javascript">
15899  */
15900  
15901 /**
15902  * @class Roo.XComponent
15903  * A delayed Element creator...
15904  * Or a way to group chunks of interface together.
15905  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15906  *  used in conjunction with XComponent.build() it will create an instance of each element,
15907  *  then call addxtype() to build the User interface.
15908  * 
15909  * Mypart.xyx = new Roo.XComponent({
15910
15911     parent : 'Mypart.xyz', // empty == document.element.!!
15912     order : '001',
15913     name : 'xxxx'
15914     region : 'xxxx'
15915     disabled : function() {} 
15916      
15917     tree : function() { // return an tree of xtype declared components
15918         var MODULE = this;
15919         return 
15920         {
15921             xtype : 'NestedLayoutPanel',
15922             // technicall
15923         }
15924      ]
15925  *})
15926  *
15927  *
15928  * It can be used to build a big heiracy, with parent etc.
15929  * or you can just use this to render a single compoent to a dom element
15930  * MYPART.render(Roo.Element | String(id) | dom_element )
15931  *
15932  *
15933  * Usage patterns.
15934  *
15935  * Classic Roo
15936  *
15937  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15938  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15939  *
15940  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15941  *
15942  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15943  * - if mulitple topModules exist, the last one is defined as the top module.
15944  *
15945  * Embeded Roo
15946  * 
15947  * When the top level or multiple modules are to embedded into a existing HTML page,
15948  * the parent element can container '#id' of the element where the module will be drawn.
15949  *
15950  * Bootstrap Roo
15951  *
15952  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15953  * it relies more on a include mechanism, where sub modules are included into an outer page.
15954  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15955  * 
15956  * Bootstrap Roo Included elements
15957  *
15958  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15959  * hence confusing the component builder as it thinks there are multiple top level elements. 
15960  *
15961  * 
15962  * 
15963  * @extends Roo.util.Observable
15964  * @constructor
15965  * @param cfg {Object} configuration of component
15966  * 
15967  */
15968 Roo.XComponent = function(cfg) {
15969     Roo.apply(this, cfg);
15970     this.addEvents({ 
15971         /**
15972              * @event built
15973              * Fires when this the componnt is built
15974              * @param {Roo.XComponent} c the component
15975              */
15976         'built' : true
15977         
15978     });
15979     this.region = this.region || 'center'; // default..
15980     Roo.XComponent.register(this);
15981     this.modules = false;
15982     this.el = false; // where the layout goes..
15983     
15984     
15985 }
15986 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15987     /**
15988      * @property el
15989      * The created element (with Roo.factory())
15990      * @type {Roo.Layout}
15991      */
15992     el  : false,
15993     
15994     /**
15995      * @property el
15996      * for BC  - use el in new code
15997      * @type {Roo.Layout}
15998      */
15999     panel : false,
16000     
16001     /**
16002      * @property layout
16003      * for BC  - use el in new code
16004      * @type {Roo.Layout}
16005      */
16006     layout : false,
16007     
16008      /**
16009      * @cfg {Function|boolean} disabled
16010      * If this module is disabled by some rule, return true from the funtion
16011      */
16012     disabled : false,
16013     
16014     /**
16015      * @cfg {String} parent 
16016      * Name of parent element which it get xtype added to..
16017      */
16018     parent: false,
16019     
16020     /**
16021      * @cfg {String} order
16022      * Used to set the order in which elements are created (usefull for multiple tabs)
16023      */
16024     
16025     order : false,
16026     /**
16027      * @cfg {String} name
16028      * String to display while loading.
16029      */
16030     name : false,
16031     /**
16032      * @cfg {String} region
16033      * Region to render component to (defaults to center)
16034      */
16035     region : 'center',
16036     
16037     /**
16038      * @cfg {Array} items
16039      * A single item array - the first element is the root of the tree..
16040      * It's done this way to stay compatible with the Xtype system...
16041      */
16042     items : false,
16043     
16044     /**
16045      * @property _tree
16046      * The method that retuns the tree of parts that make up this compoennt 
16047      * @type {function}
16048      */
16049     _tree  : false,
16050     
16051      /**
16052      * render
16053      * render element to dom or tree
16054      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16055      */
16056     
16057     render : function(el)
16058     {
16059         
16060         el = el || false;
16061         var hp = this.parent ? 1 : 0;
16062         Roo.debug &&  Roo.log(this);
16063         
16064         var tree = this._tree ? this._tree() : this.tree();
16065
16066         
16067         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16068             // if parent is a '#.....' string, then let's use that..
16069             var ename = this.parent.substr(1);
16070             this.parent = false;
16071             Roo.debug && Roo.log(ename);
16072             switch (ename) {
16073                 case 'bootstrap-body':
16074                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16075                         // this is the BorderLayout standard?
16076                        this.parent = { el : true };
16077                        break;
16078                     }
16079                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16080                         // need to insert stuff...
16081                         this.parent =  {
16082                              el : new Roo.bootstrap.layout.Border({
16083                                  el : document.body, 
16084                      
16085                                  center: {
16086                                     titlebar: false,
16087                                     autoScroll:false,
16088                                     closeOnTab: true,
16089                                     tabPosition: 'top',
16090                                       //resizeTabs: true,
16091                                     alwaysShowTabs: true,
16092                                     hideTabs: false
16093                                      //minTabWidth: 140
16094                                  }
16095                              })
16096                         
16097                          };
16098                          break;
16099                     }
16100                          
16101                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16102                         this.parent = { el :  new  Roo.bootstrap.Body() };
16103                         Roo.debug && Roo.log("setting el to doc body");
16104                          
16105                     } else {
16106                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16107                     }
16108                     break;
16109                 case 'bootstrap':
16110                     this.parent = { el : true};
16111                     // fall through
16112                 default:
16113                     el = Roo.get(ename);
16114                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16115                         this.parent = { el : true};
16116                     }
16117                     
16118                     break;
16119             }
16120                 
16121             
16122             if (!el && !this.parent) {
16123                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16124                 return;
16125             }
16126         }
16127         
16128         Roo.debug && Roo.log("EL:");
16129         Roo.debug && Roo.log(el);
16130         Roo.debug && Roo.log("this.parent.el:");
16131         Roo.debug && Roo.log(this.parent.el);
16132         
16133
16134         // altertive root elements ??? - we need a better way to indicate these.
16135         var is_alt = Roo.XComponent.is_alt ||
16136                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16137                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16138                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16139         
16140         
16141         
16142         if (!this.parent && is_alt) {
16143             //el = Roo.get(document.body);
16144             this.parent = { el : true };
16145         }
16146             
16147             
16148         
16149         if (!this.parent) {
16150             
16151             Roo.debug && Roo.log("no parent - creating one");
16152             
16153             el = el ? Roo.get(el) : false;      
16154             
16155             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16156                 
16157                 this.parent =  {
16158                     el : new Roo.bootstrap.layout.Border({
16159                         el: el || document.body,
16160                     
16161                         center: {
16162                             titlebar: false,
16163                             autoScroll:false,
16164                             closeOnTab: true,
16165                             tabPosition: 'top',
16166                              //resizeTabs: true,
16167                             alwaysShowTabs: false,
16168                             hideTabs: true,
16169                             minTabWidth: 140,
16170                             overflow: 'visible'
16171                          }
16172                      })
16173                 };
16174             } else {
16175             
16176                 // it's a top level one..
16177                 this.parent =  {
16178                     el : new Roo.BorderLayout(el || document.body, {
16179                         center: {
16180                             titlebar: false,
16181                             autoScroll:false,
16182                             closeOnTab: true,
16183                             tabPosition: 'top',
16184                              //resizeTabs: true,
16185                             alwaysShowTabs: el && hp? false :  true,
16186                             hideTabs: el || !hp ? true :  false,
16187                             minTabWidth: 140
16188                          }
16189                     })
16190                 };
16191             }
16192         }
16193         
16194         if (!this.parent.el) {
16195                 // probably an old style ctor, which has been disabled.
16196                 return;
16197
16198         }
16199                 // The 'tree' method is  '_tree now' 
16200             
16201         tree.region = tree.region || this.region;
16202         var is_body = false;
16203         if (this.parent.el === true) {
16204             // bootstrap... - body..
16205             if (el) {
16206                 tree.el = el;
16207             }
16208             this.parent.el = Roo.factory(tree);
16209             is_body = true;
16210         }
16211         
16212         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16213         this.fireEvent('built', this);
16214         
16215         this.panel = this.el;
16216         this.layout = this.panel.layout;
16217         this.parentLayout = this.parent.layout  || false;  
16218          
16219     }
16220     
16221 });
16222
16223 Roo.apply(Roo.XComponent, {
16224     /**
16225      * @property  hideProgress
16226      * true to disable the building progress bar.. usefull on single page renders.
16227      * @type Boolean
16228      */
16229     hideProgress : false,
16230     /**
16231      * @property  buildCompleted
16232      * True when the builder has completed building the interface.
16233      * @type Boolean
16234      */
16235     buildCompleted : false,
16236      
16237     /**
16238      * @property  topModule
16239      * the upper most module - uses document.element as it's constructor.
16240      * @type Object
16241      */
16242      
16243     topModule  : false,
16244       
16245     /**
16246      * @property  modules
16247      * array of modules to be created by registration system.
16248      * @type {Array} of Roo.XComponent
16249      */
16250     
16251     modules : [],
16252     /**
16253      * @property  elmodules
16254      * array of modules to be created by which use #ID 
16255      * @type {Array} of Roo.XComponent
16256      */
16257      
16258     elmodules : [],
16259
16260      /**
16261      * @property  is_alt
16262      * Is an alternative Root - normally used by bootstrap or other systems,
16263      *    where the top element in the tree can wrap 'body' 
16264      * @type {boolean}  (default false)
16265      */
16266      
16267     is_alt : false,
16268     /**
16269      * @property  build_from_html
16270      * Build elements from html - used by bootstrap HTML stuff 
16271      *    - this is cleared after build is completed
16272      * @type {boolean}    (default false)
16273      */
16274      
16275     build_from_html : false,
16276     /**
16277      * Register components to be built later.
16278      *
16279      * This solves the following issues
16280      * - Building is not done on page load, but after an authentication process has occured.
16281      * - Interface elements are registered on page load
16282      * - Parent Interface elements may not be loaded before child, so this handles that..
16283      * 
16284      *
16285      * example:
16286      * 
16287      * MyApp.register({
16288           order : '000001',
16289           module : 'Pman.Tab.projectMgr',
16290           region : 'center',
16291           parent : 'Pman.layout',
16292           disabled : false,  // or use a function..
16293         })
16294      
16295      * * @param {Object} details about module
16296      */
16297     register : function(obj) {
16298                 
16299         Roo.XComponent.event.fireEvent('register', obj);
16300         switch(typeof(obj.disabled) ) {
16301                 
16302             case 'undefined':
16303                 break;
16304             
16305             case 'function':
16306                 if ( obj.disabled() ) {
16307                         return;
16308                 }
16309                 break;
16310             
16311             default:
16312                 if (obj.disabled) {
16313                         return;
16314                 }
16315                 break;
16316         }
16317                 
16318         this.modules.push(obj);
16319          
16320     },
16321     /**
16322      * convert a string to an object..
16323      * eg. 'AAA.BBB' -> finds AAA.BBB
16324
16325      */
16326     
16327     toObject : function(str)
16328     {
16329         if (!str || typeof(str) == 'object') {
16330             return str;
16331         }
16332         if (str.substring(0,1) == '#') {
16333             return str;
16334         }
16335
16336         var ar = str.split('.');
16337         var rt, o;
16338         rt = ar.shift();
16339             /** eval:var:o */
16340         try {
16341             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16342         } catch (e) {
16343             throw "Module not found : " + str;
16344         }
16345         
16346         if (o === false) {
16347             throw "Module not found : " + str;
16348         }
16349         Roo.each(ar, function(e) {
16350             if (typeof(o[e]) == 'undefined') {
16351                 throw "Module not found : " + str;
16352             }
16353             o = o[e];
16354         });
16355         
16356         return o;
16357         
16358     },
16359     
16360     
16361     /**
16362      * move modules into their correct place in the tree..
16363      * 
16364      */
16365     preBuild : function ()
16366     {
16367         var _t = this;
16368         Roo.each(this.modules , function (obj)
16369         {
16370             Roo.XComponent.event.fireEvent('beforebuild', obj);
16371             
16372             var opar = obj.parent;
16373             try { 
16374                 obj.parent = this.toObject(opar);
16375             } catch(e) {
16376                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16377                 return;
16378             }
16379             
16380             if (!obj.parent) {
16381                 Roo.debug && Roo.log("GOT top level module");
16382                 Roo.debug && Roo.log(obj);
16383                 obj.modules = new Roo.util.MixedCollection(false, 
16384                     function(o) { return o.order + '' }
16385                 );
16386                 this.topModule = obj;
16387                 return;
16388             }
16389                         // parent is a string (usually a dom element name..)
16390             if (typeof(obj.parent) == 'string') {
16391                 this.elmodules.push(obj);
16392                 return;
16393             }
16394             if (obj.parent.constructor != Roo.XComponent) {
16395                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16396             }
16397             if (!obj.parent.modules) {
16398                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16399                     function(o) { return o.order + '' }
16400                 );
16401             }
16402             if (obj.parent.disabled) {
16403                 obj.disabled = true;
16404             }
16405             obj.parent.modules.add(obj);
16406         }, this);
16407     },
16408     
16409      /**
16410      * make a list of modules to build.
16411      * @return {Array} list of modules. 
16412      */ 
16413     
16414     buildOrder : function()
16415     {
16416         var _this = this;
16417         var cmp = function(a,b) {   
16418             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16419         };
16420         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16421             throw "No top level modules to build";
16422         }
16423         
16424         // make a flat list in order of modules to build.
16425         var mods = this.topModule ? [ this.topModule ] : [];
16426                 
16427         
16428         // elmodules (is a list of DOM based modules )
16429         Roo.each(this.elmodules, function(e) {
16430             mods.push(e);
16431             if (!this.topModule &&
16432                 typeof(e.parent) == 'string' &&
16433                 e.parent.substring(0,1) == '#' &&
16434                 Roo.get(e.parent.substr(1))
16435                ) {
16436                 
16437                 _this.topModule = e;
16438             }
16439             
16440         });
16441
16442         
16443         // add modules to their parents..
16444         var addMod = function(m) {
16445             Roo.debug && Roo.log("build Order: add: " + m.name);
16446                 
16447             mods.push(m);
16448             if (m.modules && !m.disabled) {
16449                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16450                 m.modules.keySort('ASC',  cmp );
16451                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16452     
16453                 m.modules.each(addMod);
16454             } else {
16455                 Roo.debug && Roo.log("build Order: no child modules");
16456             }
16457             // not sure if this is used any more..
16458             if (m.finalize) {
16459                 m.finalize.name = m.name + " (clean up) ";
16460                 mods.push(m.finalize);
16461             }
16462             
16463         }
16464         if (this.topModule && this.topModule.modules) { 
16465             this.topModule.modules.keySort('ASC',  cmp );
16466             this.topModule.modules.each(addMod);
16467         } 
16468         return mods;
16469     },
16470     
16471      /**
16472      * Build the registered modules.
16473      * @param {Object} parent element.
16474      * @param {Function} optional method to call after module has been added.
16475      * 
16476      */ 
16477    
16478     build : function(opts) 
16479     {
16480         
16481         if (typeof(opts) != 'undefined') {
16482             Roo.apply(this,opts);
16483         }
16484         
16485         this.preBuild();
16486         var mods = this.buildOrder();
16487       
16488         //this.allmods = mods;
16489         //Roo.debug && Roo.log(mods);
16490         //return;
16491         if (!mods.length) { // should not happen
16492             throw "NO modules!!!";
16493         }
16494         
16495         
16496         var msg = "Building Interface...";
16497         // flash it up as modal - so we store the mask!?
16498         if (!this.hideProgress && Roo.MessageBox) {
16499             Roo.MessageBox.show({ title: 'loading' });
16500             Roo.MessageBox.show({
16501                title: "Please wait...",
16502                msg: msg,
16503                width:450,
16504                progress:true,
16505                closable:false,
16506                modal: false
16507               
16508             });
16509         }
16510         var total = mods.length;
16511         
16512         var _this = this;
16513         var progressRun = function() {
16514             if (!mods.length) {
16515                 Roo.debug && Roo.log('hide?');
16516                 if (!this.hideProgress && Roo.MessageBox) {
16517                     Roo.MessageBox.hide();
16518                 }
16519                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16520                 
16521                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16522                 
16523                 // THE END...
16524                 return false;   
16525             }
16526             
16527             var m = mods.shift();
16528             
16529             
16530             Roo.debug && Roo.log(m);
16531             // not sure if this is supported any more.. - modules that are are just function
16532             if (typeof(m) == 'function') { 
16533                 m.call(this);
16534                 return progressRun.defer(10, _this);
16535             } 
16536             
16537             
16538             msg = "Building Interface " + (total  - mods.length) + 
16539                     " of " + total + 
16540                     (m.name ? (' - ' + m.name) : '');
16541                         Roo.debug && Roo.log(msg);
16542             if (!_this.hideProgress &&  Roo.MessageBox) { 
16543                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16544             }
16545             
16546          
16547             // is the module disabled?
16548             var disabled = (typeof(m.disabled) == 'function') ?
16549                 m.disabled.call(m.module.disabled) : m.disabled;    
16550             
16551             
16552             if (disabled) {
16553                 return progressRun(); // we do not update the display!
16554             }
16555             
16556             // now build 
16557             
16558                         
16559                         
16560             m.render();
16561             // it's 10 on top level, and 1 on others??? why...
16562             return progressRun.defer(10, _this);
16563              
16564         }
16565         progressRun.defer(1, _this);
16566      
16567         
16568         
16569     },
16570         
16571         
16572         /**
16573          * Event Object.
16574          *
16575          *
16576          */
16577         event: false, 
16578     /**
16579          * wrapper for event.on - aliased later..  
16580          * Typically use to register a event handler for register:
16581          *
16582          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16583          *
16584          */
16585     on : false
16586    
16587     
16588     
16589 });
16590
16591 Roo.XComponent.event = new Roo.util.Observable({
16592                 events : { 
16593                         /**
16594                          * @event register
16595                          * Fires when an Component is registered,
16596                          * set the disable property on the Component to stop registration.
16597                          * @param {Roo.XComponent} c the component being registerd.
16598                          * 
16599                          */
16600                         'register' : true,
16601             /**
16602                          * @event beforebuild
16603                          * Fires before each Component is built
16604                          * can be used to apply permissions.
16605                          * @param {Roo.XComponent} c the component being registerd.
16606                          * 
16607                          */
16608                         'beforebuild' : true,
16609                         /**
16610                          * @event buildcomplete
16611                          * Fires on the top level element when all elements have been built
16612                          * @param {Roo.XComponent} the top level component.
16613                          */
16614                         'buildcomplete' : true
16615                         
16616                 }
16617 });
16618
16619 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16620  //
16621  /**
16622  * marked - a markdown parser
16623  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16624  * https://github.com/chjj/marked
16625  */
16626
16627
16628 /**
16629  *
16630  * Roo.Markdown - is a very crude wrapper around marked..
16631  *
16632  * usage:
16633  * 
16634  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16635  * 
16636  * Note: move the sample code to the bottom of this
16637  * file before uncommenting it.
16638  *
16639  */
16640
16641 Roo.Markdown = {};
16642 Roo.Markdown.toHtml = function(text) {
16643     
16644     var c = new Roo.Markdown.marked.setOptions({
16645             renderer: new Roo.Markdown.marked.Renderer(),
16646             gfm: true,
16647             tables: true,
16648             breaks: false,
16649             pedantic: false,
16650             sanitize: false,
16651             smartLists: true,
16652             smartypants: false
16653           });
16654     // A FEW HACKS!!?
16655     
16656     text = text.replace(/\\\n/g,' ');
16657     return Roo.Markdown.marked(text);
16658 };
16659 //
16660 // converter
16661 //
16662 // Wraps all "globals" so that the only thing
16663 // exposed is makeHtml().
16664 //
16665 (function() {
16666     
16667     /**
16668      * Block-Level Grammar
16669      */
16670     
16671     var block = {
16672       newline: /^\n+/,
16673       code: /^( {4}[^\n]+\n*)+/,
16674       fences: noop,
16675       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16676       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16677       nptable: noop,
16678       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16679       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16680       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16681       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16682       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16683       table: noop,
16684       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16685       text: /^[^\n]+/
16686     };
16687     
16688     block.bullet = /(?:[*+-]|\d+\.)/;
16689     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16690     block.item = replace(block.item, 'gm')
16691       (/bull/g, block.bullet)
16692       ();
16693     
16694     block.list = replace(block.list)
16695       (/bull/g, block.bullet)
16696       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16697       ('def', '\\n+(?=' + block.def.source + ')')
16698       ();
16699     
16700     block.blockquote = replace(block.blockquote)
16701       ('def', block.def)
16702       ();
16703     
16704     block._tag = '(?!(?:'
16705       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16706       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16707       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16708     
16709     block.html = replace(block.html)
16710       ('comment', /<!--[\s\S]*?-->/)
16711       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16712       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16713       (/tag/g, block._tag)
16714       ();
16715     
16716     block.paragraph = replace(block.paragraph)
16717       ('hr', block.hr)
16718       ('heading', block.heading)
16719       ('lheading', block.lheading)
16720       ('blockquote', block.blockquote)
16721       ('tag', '<' + block._tag)
16722       ('def', block.def)
16723       ();
16724     
16725     /**
16726      * Normal Block Grammar
16727      */
16728     
16729     block.normal = merge({}, block);
16730     
16731     /**
16732      * GFM Block Grammar
16733      */
16734     
16735     block.gfm = merge({}, block.normal, {
16736       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16737       paragraph: /^/,
16738       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16739     });
16740     
16741     block.gfm.paragraph = replace(block.paragraph)
16742       ('(?!', '(?!'
16743         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16744         + block.list.source.replace('\\1', '\\3') + '|')
16745       ();
16746     
16747     /**
16748      * GFM + Tables Block Grammar
16749      */
16750     
16751     block.tables = merge({}, block.gfm, {
16752       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16753       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16754     });
16755     
16756     /**
16757      * Block Lexer
16758      */
16759     
16760     function Lexer(options) {
16761       this.tokens = [];
16762       this.tokens.links = {};
16763       this.options = options || marked.defaults;
16764       this.rules = block.normal;
16765     
16766       if (this.options.gfm) {
16767         if (this.options.tables) {
16768           this.rules = block.tables;
16769         } else {
16770           this.rules = block.gfm;
16771         }
16772       }
16773     }
16774     
16775     /**
16776      * Expose Block Rules
16777      */
16778     
16779     Lexer.rules = block;
16780     
16781     /**
16782      * Static Lex Method
16783      */
16784     
16785     Lexer.lex = function(src, options) {
16786       var lexer = new Lexer(options);
16787       return lexer.lex(src);
16788     };
16789     
16790     /**
16791      * Preprocessing
16792      */
16793     
16794     Lexer.prototype.lex = function(src) {
16795       src = src
16796         .replace(/\r\n|\r/g, '\n')
16797         .replace(/\t/g, '    ')
16798         .replace(/\u00a0/g, ' ')
16799         .replace(/\u2424/g, '\n');
16800     
16801       return this.token(src, true);
16802     };
16803     
16804     /**
16805      * Lexing
16806      */
16807     
16808     Lexer.prototype.token = function(src, top, bq) {
16809       var src = src.replace(/^ +$/gm, '')
16810         , next
16811         , loose
16812         , cap
16813         , bull
16814         , b
16815         , item
16816         , space
16817         , i
16818         , l;
16819     
16820       while (src) {
16821         // newline
16822         if (cap = this.rules.newline.exec(src)) {
16823           src = src.substring(cap[0].length);
16824           if (cap[0].length > 1) {
16825             this.tokens.push({
16826               type: 'space'
16827             });
16828           }
16829         }
16830     
16831         // code
16832         if (cap = this.rules.code.exec(src)) {
16833           src = src.substring(cap[0].length);
16834           cap = cap[0].replace(/^ {4}/gm, '');
16835           this.tokens.push({
16836             type: 'code',
16837             text: !this.options.pedantic
16838               ? cap.replace(/\n+$/, '')
16839               : cap
16840           });
16841           continue;
16842         }
16843     
16844         // fences (gfm)
16845         if (cap = this.rules.fences.exec(src)) {
16846           src = src.substring(cap[0].length);
16847           this.tokens.push({
16848             type: 'code',
16849             lang: cap[2],
16850             text: cap[3] || ''
16851           });
16852           continue;
16853         }
16854     
16855         // heading
16856         if (cap = this.rules.heading.exec(src)) {
16857           src = src.substring(cap[0].length);
16858           this.tokens.push({
16859             type: 'heading',
16860             depth: cap[1].length,
16861             text: cap[2]
16862           });
16863           continue;
16864         }
16865     
16866         // table no leading pipe (gfm)
16867         if (top && (cap = this.rules.nptable.exec(src))) {
16868           src = src.substring(cap[0].length);
16869     
16870           item = {
16871             type: 'table',
16872             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16873             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16874             cells: cap[3].replace(/\n$/, '').split('\n')
16875           };
16876     
16877           for (i = 0; i < item.align.length; i++) {
16878             if (/^ *-+: *$/.test(item.align[i])) {
16879               item.align[i] = 'right';
16880             } else if (/^ *:-+: *$/.test(item.align[i])) {
16881               item.align[i] = 'center';
16882             } else if (/^ *:-+ *$/.test(item.align[i])) {
16883               item.align[i] = 'left';
16884             } else {
16885               item.align[i] = null;
16886             }
16887           }
16888     
16889           for (i = 0; i < item.cells.length; i++) {
16890             item.cells[i] = item.cells[i].split(/ *\| */);
16891           }
16892     
16893           this.tokens.push(item);
16894     
16895           continue;
16896         }
16897     
16898         // lheading
16899         if (cap = this.rules.lheading.exec(src)) {
16900           src = src.substring(cap[0].length);
16901           this.tokens.push({
16902             type: 'heading',
16903             depth: cap[2] === '=' ? 1 : 2,
16904             text: cap[1]
16905           });
16906           continue;
16907         }
16908     
16909         // hr
16910         if (cap = this.rules.hr.exec(src)) {
16911           src = src.substring(cap[0].length);
16912           this.tokens.push({
16913             type: 'hr'
16914           });
16915           continue;
16916         }
16917     
16918         // blockquote
16919         if (cap = this.rules.blockquote.exec(src)) {
16920           src = src.substring(cap[0].length);
16921     
16922           this.tokens.push({
16923             type: 'blockquote_start'
16924           });
16925     
16926           cap = cap[0].replace(/^ *> ?/gm, '');
16927     
16928           // Pass `top` to keep the current
16929           // "toplevel" state. This is exactly
16930           // how markdown.pl works.
16931           this.token(cap, top, true);
16932     
16933           this.tokens.push({
16934             type: 'blockquote_end'
16935           });
16936     
16937           continue;
16938         }
16939     
16940         // list
16941         if (cap = this.rules.list.exec(src)) {
16942           src = src.substring(cap[0].length);
16943           bull = cap[2];
16944     
16945           this.tokens.push({
16946             type: 'list_start',
16947             ordered: bull.length > 1
16948           });
16949     
16950           // Get each top-level item.
16951           cap = cap[0].match(this.rules.item);
16952     
16953           next = false;
16954           l = cap.length;
16955           i = 0;
16956     
16957           for (; i < l; i++) {
16958             item = cap[i];
16959     
16960             // Remove the list item's bullet
16961             // so it is seen as the next token.
16962             space = item.length;
16963             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16964     
16965             // Outdent whatever the
16966             // list item contains. Hacky.
16967             if (~item.indexOf('\n ')) {
16968               space -= item.length;
16969               item = !this.options.pedantic
16970                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16971                 : item.replace(/^ {1,4}/gm, '');
16972             }
16973     
16974             // Determine whether the next list item belongs here.
16975             // Backpedal if it does not belong in this list.
16976             if (this.options.smartLists && i !== l - 1) {
16977               b = block.bullet.exec(cap[i + 1])[0];
16978               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
16979                 src = cap.slice(i + 1).join('\n') + src;
16980                 i = l - 1;
16981               }
16982             }
16983     
16984             // Determine whether item is loose or not.
16985             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
16986             // for discount behavior.
16987             loose = next || /\n\n(?!\s*$)/.test(item);
16988             if (i !== l - 1) {
16989               next = item.charAt(item.length - 1) === '\n';
16990               if (!loose) { loose = next; }
16991             }
16992     
16993             this.tokens.push({
16994               type: loose
16995                 ? 'loose_item_start'
16996                 : 'list_item_start'
16997             });
16998     
16999             // Recurse.
17000             this.token(item, false, bq);
17001     
17002             this.tokens.push({
17003               type: 'list_item_end'
17004             });
17005           }
17006     
17007           this.tokens.push({
17008             type: 'list_end'
17009           });
17010     
17011           continue;
17012         }
17013     
17014         // html
17015         if (cap = this.rules.html.exec(src)) {
17016           src = src.substring(cap[0].length);
17017           this.tokens.push({
17018             type: this.options.sanitize
17019               ? 'paragraph'
17020               : 'html',
17021             pre: !this.options.sanitizer
17022               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17023             text: cap[0]
17024           });
17025           continue;
17026         }
17027     
17028         // def
17029         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17030           src = src.substring(cap[0].length);
17031           this.tokens.links[cap[1].toLowerCase()] = {
17032             href: cap[2],
17033             title: cap[3]
17034           };
17035           continue;
17036         }
17037     
17038         // table (gfm)
17039         if (top && (cap = this.rules.table.exec(src))) {
17040           src = src.substring(cap[0].length);
17041     
17042           item = {
17043             type: 'table',
17044             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17045             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17046             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17047           };
17048     
17049           for (i = 0; i < item.align.length; i++) {
17050             if (/^ *-+: *$/.test(item.align[i])) {
17051               item.align[i] = 'right';
17052             } else if (/^ *:-+: *$/.test(item.align[i])) {
17053               item.align[i] = 'center';
17054             } else if (/^ *:-+ *$/.test(item.align[i])) {
17055               item.align[i] = 'left';
17056             } else {
17057               item.align[i] = null;
17058             }
17059           }
17060     
17061           for (i = 0; i < item.cells.length; i++) {
17062             item.cells[i] = item.cells[i]
17063               .replace(/^ *\| *| *\| *$/g, '')
17064               .split(/ *\| */);
17065           }
17066     
17067           this.tokens.push(item);
17068     
17069           continue;
17070         }
17071     
17072         // top-level paragraph
17073         if (top && (cap = this.rules.paragraph.exec(src))) {
17074           src = src.substring(cap[0].length);
17075           this.tokens.push({
17076             type: 'paragraph',
17077             text: cap[1].charAt(cap[1].length - 1) === '\n'
17078               ? cap[1].slice(0, -1)
17079               : cap[1]
17080           });
17081           continue;
17082         }
17083     
17084         // text
17085         if (cap = this.rules.text.exec(src)) {
17086           // Top-level should never reach here.
17087           src = src.substring(cap[0].length);
17088           this.tokens.push({
17089             type: 'text',
17090             text: cap[0]
17091           });
17092           continue;
17093         }
17094     
17095         if (src) {
17096           throw new
17097             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17098         }
17099       }
17100     
17101       return this.tokens;
17102     };
17103     
17104     /**
17105      * Inline-Level Grammar
17106      */
17107     
17108     var inline = {
17109       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17110       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17111       url: noop,
17112       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17113       link: /^!?\[(inside)\]\(href\)/,
17114       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17115       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17116       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17117       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17118       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17119       br: /^ {2,}\n(?!\s*$)/,
17120       del: noop,
17121       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17122     };
17123     
17124     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17125     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17126     
17127     inline.link = replace(inline.link)
17128       ('inside', inline._inside)
17129       ('href', inline._href)
17130       ();
17131     
17132     inline.reflink = replace(inline.reflink)
17133       ('inside', inline._inside)
17134       ();
17135     
17136     /**
17137      * Normal Inline Grammar
17138      */
17139     
17140     inline.normal = merge({}, inline);
17141     
17142     /**
17143      * Pedantic Inline Grammar
17144      */
17145     
17146     inline.pedantic = merge({}, inline.normal, {
17147       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17148       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17149     });
17150     
17151     /**
17152      * GFM Inline Grammar
17153      */
17154     
17155     inline.gfm = merge({}, inline.normal, {
17156       escape: replace(inline.escape)('])', '~|])')(),
17157       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17158       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17159       text: replace(inline.text)
17160         (']|', '~]|')
17161         ('|', '|https?://|')
17162         ()
17163     });
17164     
17165     /**
17166      * GFM + Line Breaks Inline Grammar
17167      */
17168     
17169     inline.breaks = merge({}, inline.gfm, {
17170       br: replace(inline.br)('{2,}', '*')(),
17171       text: replace(inline.gfm.text)('{2,}', '*')()
17172     });
17173     
17174     /**
17175      * Inline Lexer & Compiler
17176      */
17177     
17178     function InlineLexer(links, options) {
17179       this.options = options || marked.defaults;
17180       this.links = links;
17181       this.rules = inline.normal;
17182       this.renderer = this.options.renderer || new Renderer;
17183       this.renderer.options = this.options;
17184     
17185       if (!this.links) {
17186         throw new
17187           Error('Tokens array requires a `links` property.');
17188       }
17189     
17190       if (this.options.gfm) {
17191         if (this.options.breaks) {
17192           this.rules = inline.breaks;
17193         } else {
17194           this.rules = inline.gfm;
17195         }
17196       } else if (this.options.pedantic) {
17197         this.rules = inline.pedantic;
17198       }
17199     }
17200     
17201     /**
17202      * Expose Inline Rules
17203      */
17204     
17205     InlineLexer.rules = inline;
17206     
17207     /**
17208      * Static Lexing/Compiling Method
17209      */
17210     
17211     InlineLexer.output = function(src, links, options) {
17212       var inline = new InlineLexer(links, options);
17213       return inline.output(src);
17214     };
17215     
17216     /**
17217      * Lexing/Compiling
17218      */
17219     
17220     InlineLexer.prototype.output = function(src) {
17221       var out = ''
17222         , link
17223         , text
17224         , href
17225         , cap;
17226     
17227       while (src) {
17228         // escape
17229         if (cap = this.rules.escape.exec(src)) {
17230           src = src.substring(cap[0].length);
17231           out += cap[1];
17232           continue;
17233         }
17234     
17235         // autolink
17236         if (cap = this.rules.autolink.exec(src)) {
17237           src = src.substring(cap[0].length);
17238           if (cap[2] === '@') {
17239             text = cap[1].charAt(6) === ':'
17240               ? this.mangle(cap[1].substring(7))
17241               : this.mangle(cap[1]);
17242             href = this.mangle('mailto:') + text;
17243           } else {
17244             text = escape(cap[1]);
17245             href = text;
17246           }
17247           out += this.renderer.link(href, null, text);
17248           continue;
17249         }
17250     
17251         // url (gfm)
17252         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17253           src = src.substring(cap[0].length);
17254           text = escape(cap[1]);
17255           href = text;
17256           out += this.renderer.link(href, null, text);
17257           continue;
17258         }
17259     
17260         // tag
17261         if (cap = this.rules.tag.exec(src)) {
17262           if (!this.inLink && /^<a /i.test(cap[0])) {
17263             this.inLink = true;
17264           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17265             this.inLink = false;
17266           }
17267           src = src.substring(cap[0].length);
17268           out += this.options.sanitize
17269             ? this.options.sanitizer
17270               ? this.options.sanitizer(cap[0])
17271               : escape(cap[0])
17272             : cap[0];
17273           continue;
17274         }
17275     
17276         // link
17277         if (cap = this.rules.link.exec(src)) {
17278           src = src.substring(cap[0].length);
17279           this.inLink = true;
17280           out += this.outputLink(cap, {
17281             href: cap[2],
17282             title: cap[3]
17283           });
17284           this.inLink = false;
17285           continue;
17286         }
17287     
17288         // reflink, nolink
17289         if ((cap = this.rules.reflink.exec(src))
17290             || (cap = this.rules.nolink.exec(src))) {
17291           src = src.substring(cap[0].length);
17292           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17293           link = this.links[link.toLowerCase()];
17294           if (!link || !link.href) {
17295             out += cap[0].charAt(0);
17296             src = cap[0].substring(1) + src;
17297             continue;
17298           }
17299           this.inLink = true;
17300           out += this.outputLink(cap, link);
17301           this.inLink = false;
17302           continue;
17303         }
17304     
17305         // strong
17306         if (cap = this.rules.strong.exec(src)) {
17307           src = src.substring(cap[0].length);
17308           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17309           continue;
17310         }
17311     
17312         // em
17313         if (cap = this.rules.em.exec(src)) {
17314           src = src.substring(cap[0].length);
17315           out += this.renderer.em(this.output(cap[2] || cap[1]));
17316           continue;
17317         }
17318     
17319         // code
17320         if (cap = this.rules.code.exec(src)) {
17321           src = src.substring(cap[0].length);
17322           out += this.renderer.codespan(escape(cap[2], true));
17323           continue;
17324         }
17325     
17326         // br
17327         if (cap = this.rules.br.exec(src)) {
17328           src = src.substring(cap[0].length);
17329           out += this.renderer.br();
17330           continue;
17331         }
17332     
17333         // del (gfm)
17334         if (cap = this.rules.del.exec(src)) {
17335           src = src.substring(cap[0].length);
17336           out += this.renderer.del(this.output(cap[1]));
17337           continue;
17338         }
17339     
17340         // text
17341         if (cap = this.rules.text.exec(src)) {
17342           src = src.substring(cap[0].length);
17343           out += this.renderer.text(escape(this.smartypants(cap[0])));
17344           continue;
17345         }
17346     
17347         if (src) {
17348           throw new
17349             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17350         }
17351       }
17352     
17353       return out;
17354     };
17355     
17356     /**
17357      * Compile Link
17358      */
17359     
17360     InlineLexer.prototype.outputLink = function(cap, link) {
17361       var href = escape(link.href)
17362         , title = link.title ? escape(link.title) : null;
17363     
17364       return cap[0].charAt(0) !== '!'
17365         ? this.renderer.link(href, title, this.output(cap[1]))
17366         : this.renderer.image(href, title, escape(cap[1]));
17367     };
17368     
17369     /**
17370      * Smartypants Transformations
17371      */
17372     
17373     InlineLexer.prototype.smartypants = function(text) {
17374       if (!this.options.smartypants)  { return text; }
17375       return text
17376         // em-dashes
17377         .replace(/---/g, '\u2014')
17378         // en-dashes
17379         .replace(/--/g, '\u2013')
17380         // opening singles
17381         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17382         // closing singles & apostrophes
17383         .replace(/'/g, '\u2019')
17384         // opening doubles
17385         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17386         // closing doubles
17387         .replace(/"/g, '\u201d')
17388         // ellipses
17389         .replace(/\.{3}/g, '\u2026');
17390     };
17391     
17392     /**
17393      * Mangle Links
17394      */
17395     
17396     InlineLexer.prototype.mangle = function(text) {
17397       if (!this.options.mangle) { return text; }
17398       var out = ''
17399         , l = text.length
17400         , i = 0
17401         , ch;
17402     
17403       for (; i < l; i++) {
17404         ch = text.charCodeAt(i);
17405         if (Math.random() > 0.5) {
17406           ch = 'x' + ch.toString(16);
17407         }
17408         out += '&#' + ch + ';';
17409       }
17410     
17411       return out;
17412     };
17413     
17414     /**
17415      * Renderer
17416      */
17417     
17418     function Renderer(options) {
17419       this.options = options || {};
17420     }
17421     
17422     Renderer.prototype.code = function(code, lang, escaped) {
17423       if (this.options.highlight) {
17424         var out = this.options.highlight(code, lang);
17425         if (out != null && out !== code) {
17426           escaped = true;
17427           code = out;
17428         }
17429       } else {
17430             // hack!!! - it's already escapeD?
17431             escaped = true;
17432       }
17433     
17434       if (!lang) {
17435         return '<pre><code>'
17436           + (escaped ? code : escape(code, true))
17437           + '\n</code></pre>';
17438       }
17439     
17440       return '<pre><code class="'
17441         + this.options.langPrefix
17442         + escape(lang, true)
17443         + '">'
17444         + (escaped ? code : escape(code, true))
17445         + '\n</code></pre>\n';
17446     };
17447     
17448     Renderer.prototype.blockquote = function(quote) {
17449       return '<blockquote>\n' + quote + '</blockquote>\n';
17450     };
17451     
17452     Renderer.prototype.html = function(html) {
17453       return html;
17454     };
17455     
17456     Renderer.prototype.heading = function(text, level, raw) {
17457       return '<h'
17458         + level
17459         + ' id="'
17460         + this.options.headerPrefix
17461         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17462         + '">'
17463         + text
17464         + '</h'
17465         + level
17466         + '>\n';
17467     };
17468     
17469     Renderer.prototype.hr = function() {
17470       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17471     };
17472     
17473     Renderer.prototype.list = function(body, ordered) {
17474       var type = ordered ? 'ol' : 'ul';
17475       return '<' + type + '>\n' + body + '</' + type + '>\n';
17476     };
17477     
17478     Renderer.prototype.listitem = function(text) {
17479       return '<li>' + text + '</li>\n';
17480     };
17481     
17482     Renderer.prototype.paragraph = function(text) {
17483       return '<p>' + text + '</p>\n';
17484     };
17485     
17486     Renderer.prototype.table = function(header, body) {
17487       return '<table class="table table-striped">\n'
17488         + '<thead>\n'
17489         + header
17490         + '</thead>\n'
17491         + '<tbody>\n'
17492         + body
17493         + '</tbody>\n'
17494         + '</table>\n';
17495     };
17496     
17497     Renderer.prototype.tablerow = function(content) {
17498       return '<tr>\n' + content + '</tr>\n';
17499     };
17500     
17501     Renderer.prototype.tablecell = function(content, flags) {
17502       var type = flags.header ? 'th' : 'td';
17503       var tag = flags.align
17504         ? '<' + type + ' style="text-align:' + flags.align + '">'
17505         : '<' + type + '>';
17506       return tag + content + '</' + type + '>\n';
17507     };
17508     
17509     // span level renderer
17510     Renderer.prototype.strong = function(text) {
17511       return '<strong>' + text + '</strong>';
17512     };
17513     
17514     Renderer.prototype.em = function(text) {
17515       return '<em>' + text + '</em>';
17516     };
17517     
17518     Renderer.prototype.codespan = function(text) {
17519       return '<code>' + text + '</code>';
17520     };
17521     
17522     Renderer.prototype.br = function() {
17523       return this.options.xhtml ? '<br/>' : '<br>';
17524     };
17525     
17526     Renderer.prototype.del = function(text) {
17527       return '<del>' + text + '</del>';
17528     };
17529     
17530     Renderer.prototype.link = function(href, title, text) {
17531       if (this.options.sanitize) {
17532         try {
17533           var prot = decodeURIComponent(unescape(href))
17534             .replace(/[^\w:]/g, '')
17535             .toLowerCase();
17536         } catch (e) {
17537           return '';
17538         }
17539         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17540           return '';
17541         }
17542       }
17543       var out = '<a href="' + href + '"';
17544       if (title) {
17545         out += ' title="' + title + '"';
17546       }
17547       out += '>' + text + '</a>';
17548       return out;
17549     };
17550     
17551     Renderer.prototype.image = function(href, title, text) {
17552       var out = '<img src="' + href + '" alt="' + text + '"';
17553       if (title) {
17554         out += ' title="' + title + '"';
17555       }
17556       out += this.options.xhtml ? '/>' : '>';
17557       return out;
17558     };
17559     
17560     Renderer.prototype.text = function(text) {
17561       return text;
17562     };
17563     
17564     /**
17565      * Parsing & Compiling
17566      */
17567     
17568     function Parser(options) {
17569       this.tokens = [];
17570       this.token = null;
17571       this.options = options || marked.defaults;
17572       this.options.renderer = this.options.renderer || new Renderer;
17573       this.renderer = this.options.renderer;
17574       this.renderer.options = this.options;
17575     }
17576     
17577     /**
17578      * Static Parse Method
17579      */
17580     
17581     Parser.parse = function(src, options, renderer) {
17582       var parser = new Parser(options, renderer);
17583       return parser.parse(src);
17584     };
17585     
17586     /**
17587      * Parse Loop
17588      */
17589     
17590     Parser.prototype.parse = function(src) {
17591       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17592       this.tokens = src.reverse();
17593     
17594       var out = '';
17595       while (this.next()) {
17596         out += this.tok();
17597       }
17598     
17599       return out;
17600     };
17601     
17602     /**
17603      * Next Token
17604      */
17605     
17606     Parser.prototype.next = function() {
17607       return this.token = this.tokens.pop();
17608     };
17609     
17610     /**
17611      * Preview Next Token
17612      */
17613     
17614     Parser.prototype.peek = function() {
17615       return this.tokens[this.tokens.length - 1] || 0;
17616     };
17617     
17618     /**
17619      * Parse Text Tokens
17620      */
17621     
17622     Parser.prototype.parseText = function() {
17623       var body = this.token.text;
17624     
17625       while (this.peek().type === 'text') {
17626         body += '\n' + this.next().text;
17627       }
17628     
17629       return this.inline.output(body);
17630     };
17631     
17632     /**
17633      * Parse Current Token
17634      */
17635     
17636     Parser.prototype.tok = function() {
17637       switch (this.token.type) {
17638         case 'space': {
17639           return '';
17640         }
17641         case 'hr': {
17642           return this.renderer.hr();
17643         }
17644         case 'heading': {
17645           return this.renderer.heading(
17646             this.inline.output(this.token.text),
17647             this.token.depth,
17648             this.token.text);
17649         }
17650         case 'code': {
17651           return this.renderer.code(this.token.text,
17652             this.token.lang,
17653             this.token.escaped);
17654         }
17655         case 'table': {
17656           var header = ''
17657             , body = ''
17658             , i
17659             , row
17660             , cell
17661             , flags
17662             , j;
17663     
17664           // header
17665           cell = '';
17666           for (i = 0; i < this.token.header.length; i++) {
17667             flags = { header: true, align: this.token.align[i] };
17668             cell += this.renderer.tablecell(
17669               this.inline.output(this.token.header[i]),
17670               { header: true, align: this.token.align[i] }
17671             );
17672           }
17673           header += this.renderer.tablerow(cell);
17674     
17675           for (i = 0; i < this.token.cells.length; i++) {
17676             row = this.token.cells[i];
17677     
17678             cell = '';
17679             for (j = 0; j < row.length; j++) {
17680               cell += this.renderer.tablecell(
17681                 this.inline.output(row[j]),
17682                 { header: false, align: this.token.align[j] }
17683               );
17684             }
17685     
17686             body += this.renderer.tablerow(cell);
17687           }
17688           return this.renderer.table(header, body);
17689         }
17690         case 'blockquote_start': {
17691           var body = '';
17692     
17693           while (this.next().type !== 'blockquote_end') {
17694             body += this.tok();
17695           }
17696     
17697           return this.renderer.blockquote(body);
17698         }
17699         case 'list_start': {
17700           var body = ''
17701             , ordered = this.token.ordered;
17702     
17703           while (this.next().type !== 'list_end') {
17704             body += this.tok();
17705           }
17706     
17707           return this.renderer.list(body, ordered);
17708         }
17709         case 'list_item_start': {
17710           var body = '';
17711     
17712           while (this.next().type !== 'list_item_end') {
17713             body += this.token.type === 'text'
17714               ? this.parseText()
17715               : this.tok();
17716           }
17717     
17718           return this.renderer.listitem(body);
17719         }
17720         case 'loose_item_start': {
17721           var body = '';
17722     
17723           while (this.next().type !== 'list_item_end') {
17724             body += this.tok();
17725           }
17726     
17727           return this.renderer.listitem(body);
17728         }
17729         case 'html': {
17730           var html = !this.token.pre && !this.options.pedantic
17731             ? this.inline.output(this.token.text)
17732             : this.token.text;
17733           return this.renderer.html(html);
17734         }
17735         case 'paragraph': {
17736           return this.renderer.paragraph(this.inline.output(this.token.text));
17737         }
17738         case 'text': {
17739           return this.renderer.paragraph(this.parseText());
17740         }
17741       }
17742     };
17743     
17744     /**
17745      * Helpers
17746      */
17747     
17748     function escape(html, encode) {
17749       return html
17750         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17751         .replace(/</g, '&lt;')
17752         .replace(/>/g, '&gt;')
17753         .replace(/"/g, '&quot;')
17754         .replace(/'/g, '&#39;');
17755     }
17756     
17757     function unescape(html) {
17758         // explicitly match decimal, hex, and named HTML entities 
17759       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17760         n = n.toLowerCase();
17761         if (n === 'colon') { return ':'; }
17762         if (n.charAt(0) === '#') {
17763           return n.charAt(1) === 'x'
17764             ? String.fromCharCode(parseInt(n.substring(2), 16))
17765             : String.fromCharCode(+n.substring(1));
17766         }
17767         return '';
17768       });
17769     }
17770     
17771     function replace(regex, opt) {
17772       regex = regex.source;
17773       opt = opt || '';
17774       return function self(name, val) {
17775         if (!name) { return new RegExp(regex, opt); }
17776         val = val.source || val;
17777         val = val.replace(/(^|[^\[])\^/g, '$1');
17778         regex = regex.replace(name, val);
17779         return self;
17780       };
17781     }
17782     
17783     function noop() {}
17784     noop.exec = noop;
17785     
17786     function merge(obj) {
17787       var i = 1
17788         , target
17789         , key;
17790     
17791       for (; i < arguments.length; i++) {
17792         target = arguments[i];
17793         for (key in target) {
17794           if (Object.prototype.hasOwnProperty.call(target, key)) {
17795             obj[key] = target[key];
17796           }
17797         }
17798       }
17799     
17800       return obj;
17801     }
17802     
17803     
17804     /**
17805      * Marked
17806      */
17807     
17808     function marked(src, opt, callback) {
17809       if (callback || typeof opt === 'function') {
17810         if (!callback) {
17811           callback = opt;
17812           opt = null;
17813         }
17814     
17815         opt = merge({}, marked.defaults, opt || {});
17816     
17817         var highlight = opt.highlight
17818           , tokens
17819           , pending
17820           , i = 0;
17821     
17822         try {
17823           tokens = Lexer.lex(src, opt)
17824         } catch (e) {
17825           return callback(e);
17826         }
17827     
17828         pending = tokens.length;
17829     
17830         var done = function(err) {
17831           if (err) {
17832             opt.highlight = highlight;
17833             return callback(err);
17834           }
17835     
17836           var out;
17837     
17838           try {
17839             out = Parser.parse(tokens, opt);
17840           } catch (e) {
17841             err = e;
17842           }
17843     
17844           opt.highlight = highlight;
17845     
17846           return err
17847             ? callback(err)
17848             : callback(null, out);
17849         };
17850     
17851         if (!highlight || highlight.length < 3) {
17852           return done();
17853         }
17854     
17855         delete opt.highlight;
17856     
17857         if (!pending) { return done(); }
17858     
17859         for (; i < tokens.length; i++) {
17860           (function(token) {
17861             if (token.type !== 'code') {
17862               return --pending || done();
17863             }
17864             return highlight(token.text, token.lang, function(err, code) {
17865               if (err) { return done(err); }
17866               if (code == null || code === token.text) {
17867                 return --pending || done();
17868               }
17869               token.text = code;
17870               token.escaped = true;
17871               --pending || done();
17872             });
17873           })(tokens[i]);
17874         }
17875     
17876         return;
17877       }
17878       try {
17879         if (opt) { opt = merge({}, marked.defaults, opt); }
17880         return Parser.parse(Lexer.lex(src, opt), opt);
17881       } catch (e) {
17882         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17883         if ((opt || marked.defaults).silent) {
17884           return '<p>An error occured:</p><pre>'
17885             + escape(e.message + '', true)
17886             + '</pre>';
17887         }
17888         throw e;
17889       }
17890     }
17891     
17892     /**
17893      * Options
17894      */
17895     
17896     marked.options =
17897     marked.setOptions = function(opt) {
17898       merge(marked.defaults, opt);
17899       return marked;
17900     };
17901     
17902     marked.defaults = {
17903       gfm: true,
17904       tables: true,
17905       breaks: false,
17906       pedantic: false,
17907       sanitize: false,
17908       sanitizer: null,
17909       mangle: true,
17910       smartLists: false,
17911       silent: false,
17912       highlight: null,
17913       langPrefix: 'lang-',
17914       smartypants: false,
17915       headerPrefix: '',
17916       renderer: new Renderer,
17917       xhtml: false
17918     };
17919     
17920     /**
17921      * Expose
17922      */
17923     
17924     marked.Parser = Parser;
17925     marked.parser = Parser.parse;
17926     
17927     marked.Renderer = Renderer;
17928     
17929     marked.Lexer = Lexer;
17930     marked.lexer = Lexer.lex;
17931     
17932     marked.InlineLexer = InlineLexer;
17933     marked.inlineLexer = InlineLexer.output;
17934     
17935     marked.parse = marked;
17936     
17937     Roo.Markdown.marked = marked;
17938
17939 })();/*
17940  * Based on:
17941  * Ext JS Library 1.1.1
17942  * Copyright(c) 2006-2007, Ext JS, LLC.
17943  *
17944  * Originally Released Under LGPL - original licence link has changed is not relivant.
17945  *
17946  * Fork - LGPL
17947  * <script type="text/javascript">
17948  */
17949
17950
17951
17952 /*
17953  * These classes are derivatives of the similarly named classes in the YUI Library.
17954  * The original license:
17955  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17956  * Code licensed under the BSD License:
17957  * http://developer.yahoo.net/yui/license.txt
17958  */
17959
17960 (function() {
17961
17962 var Event=Roo.EventManager;
17963 var Dom=Roo.lib.Dom;
17964
17965 /**
17966  * @class Roo.dd.DragDrop
17967  * @extends Roo.util.Observable
17968  * Defines the interface and base operation of items that that can be
17969  * dragged or can be drop targets.  It was designed to be extended, overriding
17970  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17971  * Up to three html elements can be associated with a DragDrop instance:
17972  * <ul>
17973  * <li>linked element: the element that is passed into the constructor.
17974  * This is the element which defines the boundaries for interaction with
17975  * other DragDrop objects.</li>
17976  * <li>handle element(s): The drag operation only occurs if the element that
17977  * was clicked matches a handle element.  By default this is the linked
17978  * element, but there are times that you will want only a portion of the
17979  * linked element to initiate the drag operation, and the setHandleElId()
17980  * method provides a way to define this.</li>
17981  * <li>drag element: this represents the element that would be moved along
17982  * with the cursor during a drag operation.  By default, this is the linked
17983  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
17984  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
17985  * </li>
17986  * </ul>
17987  * This class should not be instantiated until the onload event to ensure that
17988  * the associated elements are available.
17989  * The following would define a DragDrop obj that would interact with any
17990  * other DragDrop obj in the "group1" group:
17991  * <pre>
17992  *  dd = new Roo.dd.DragDrop("div1", "group1");
17993  * </pre>
17994  * Since none of the event handlers have been implemented, nothing would
17995  * actually happen if you were to run the code above.  Normally you would
17996  * override this class or one of the default implementations, but you can
17997  * also override the methods you want on an instance of the class...
17998  * <pre>
17999  *  dd.onDragDrop = function(e, id) {
18000  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18001  *  }
18002  * </pre>
18003  * @constructor
18004  * @param {String} id of the element that is linked to this instance
18005  * @param {String} sGroup the group of related DragDrop objects
18006  * @param {object} config an object containing configurable attributes
18007  *                Valid properties for DragDrop:
18008  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18009  */
18010 Roo.dd.DragDrop = function(id, sGroup, config) {
18011     if (id) {
18012         this.init(id, sGroup, config);
18013     }
18014     
18015 };
18016
18017 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18018
18019     /**
18020      * The id of the element associated with this object.  This is what we
18021      * refer to as the "linked element" because the size and position of
18022      * this element is used to determine when the drag and drop objects have
18023      * interacted.
18024      * @property id
18025      * @type String
18026      */
18027     id: null,
18028
18029     /**
18030      * Configuration attributes passed into the constructor
18031      * @property config
18032      * @type object
18033      */
18034     config: null,
18035
18036     /**
18037      * The id of the element that will be dragged.  By default this is same
18038      * as the linked element , but could be changed to another element. Ex:
18039      * Roo.dd.DDProxy
18040      * @property dragElId
18041      * @type String
18042      * @private
18043      */
18044     dragElId: null,
18045
18046     /**
18047      * the id of the element that initiates the drag operation.  By default
18048      * this is the linked element, but could be changed to be a child of this
18049      * element.  This lets us do things like only starting the drag when the
18050      * header element within the linked html element is clicked.
18051      * @property handleElId
18052      * @type String
18053      * @private
18054      */
18055     handleElId: null,
18056
18057     /**
18058      * An associative array of HTML tags that will be ignored if clicked.
18059      * @property invalidHandleTypes
18060      * @type {string: string}
18061      */
18062     invalidHandleTypes: null,
18063
18064     /**
18065      * An associative array of ids for elements that will be ignored if clicked
18066      * @property invalidHandleIds
18067      * @type {string: string}
18068      */
18069     invalidHandleIds: null,
18070
18071     /**
18072      * An indexted array of css class names for elements that will be ignored
18073      * if clicked.
18074      * @property invalidHandleClasses
18075      * @type string[]
18076      */
18077     invalidHandleClasses: null,
18078
18079     /**
18080      * The linked element's absolute X position at the time the drag was
18081      * started
18082      * @property startPageX
18083      * @type int
18084      * @private
18085      */
18086     startPageX: 0,
18087
18088     /**
18089      * The linked element's absolute X position at the time the drag was
18090      * started
18091      * @property startPageY
18092      * @type int
18093      * @private
18094      */
18095     startPageY: 0,
18096
18097     /**
18098      * The group defines a logical collection of DragDrop objects that are
18099      * related.  Instances only get events when interacting with other
18100      * DragDrop object in the same group.  This lets us define multiple
18101      * groups using a single DragDrop subclass if we want.
18102      * @property groups
18103      * @type {string: string}
18104      */
18105     groups: null,
18106
18107     /**
18108      * Individual drag/drop instances can be locked.  This will prevent
18109      * onmousedown start drag.
18110      * @property locked
18111      * @type boolean
18112      * @private
18113      */
18114     locked: false,
18115
18116     /**
18117      * Lock this instance
18118      * @method lock
18119      */
18120     lock: function() { this.locked = true; },
18121
18122     /**
18123      * Unlock this instace
18124      * @method unlock
18125      */
18126     unlock: function() { this.locked = false; },
18127
18128     /**
18129      * By default, all insances can be a drop target.  This can be disabled by
18130      * setting isTarget to false.
18131      * @method isTarget
18132      * @type boolean
18133      */
18134     isTarget: true,
18135
18136     /**
18137      * The padding configured for this drag and drop object for calculating
18138      * the drop zone intersection with this object.
18139      * @method padding
18140      * @type int[]
18141      */
18142     padding: null,
18143
18144     /**
18145      * Cached reference to the linked element
18146      * @property _domRef
18147      * @private
18148      */
18149     _domRef: null,
18150
18151     /**
18152      * Internal typeof flag
18153      * @property __ygDragDrop
18154      * @private
18155      */
18156     __ygDragDrop: true,
18157
18158     /**
18159      * Set to true when horizontal contraints are applied
18160      * @property constrainX
18161      * @type boolean
18162      * @private
18163      */
18164     constrainX: false,
18165
18166     /**
18167      * Set to true when vertical contraints are applied
18168      * @property constrainY
18169      * @type boolean
18170      * @private
18171      */
18172     constrainY: false,
18173
18174     /**
18175      * The left constraint
18176      * @property minX
18177      * @type int
18178      * @private
18179      */
18180     minX: 0,
18181
18182     /**
18183      * The right constraint
18184      * @property maxX
18185      * @type int
18186      * @private
18187      */
18188     maxX: 0,
18189
18190     /**
18191      * The up constraint
18192      * @property minY
18193      * @type int
18194      * @type int
18195      * @private
18196      */
18197     minY: 0,
18198
18199     /**
18200      * The down constraint
18201      * @property maxY
18202      * @type int
18203      * @private
18204      */
18205     maxY: 0,
18206
18207     /**
18208      * Maintain offsets when we resetconstraints.  Set to true when you want
18209      * the position of the element relative to its parent to stay the same
18210      * when the page changes
18211      *
18212      * @property maintainOffset
18213      * @type boolean
18214      */
18215     maintainOffset: false,
18216
18217     /**
18218      * Array of pixel locations the element will snap to if we specified a
18219      * horizontal graduation/interval.  This array is generated automatically
18220      * when you define a tick interval.
18221      * @property xTicks
18222      * @type int[]
18223      */
18224     xTicks: null,
18225
18226     /**
18227      * Array of pixel locations the element will snap to if we specified a
18228      * vertical graduation/interval.  This array is generated automatically
18229      * when you define a tick interval.
18230      * @property yTicks
18231      * @type int[]
18232      */
18233     yTicks: null,
18234
18235     /**
18236      * By default the drag and drop instance will only respond to the primary
18237      * button click (left button for a right-handed mouse).  Set to true to
18238      * allow drag and drop to start with any mouse click that is propogated
18239      * by the browser
18240      * @property primaryButtonOnly
18241      * @type boolean
18242      */
18243     primaryButtonOnly: true,
18244
18245     /**
18246      * The availabe property is false until the linked dom element is accessible.
18247      * @property available
18248      * @type boolean
18249      */
18250     available: false,
18251
18252     /**
18253      * By default, drags can only be initiated if the mousedown occurs in the
18254      * region the linked element is.  This is done in part to work around a
18255      * bug in some browsers that mis-report the mousedown if the previous
18256      * mouseup happened outside of the window.  This property is set to true
18257      * if outer handles are defined.
18258      *
18259      * @property hasOuterHandles
18260      * @type boolean
18261      * @default false
18262      */
18263     hasOuterHandles: false,
18264
18265     /**
18266      * Code that executes immediately before the startDrag event
18267      * @method b4StartDrag
18268      * @private
18269      */
18270     b4StartDrag: function(x, y) { },
18271
18272     /**
18273      * Abstract method called after a drag/drop object is clicked
18274      * and the drag or mousedown time thresholds have beeen met.
18275      * @method startDrag
18276      * @param {int} X click location
18277      * @param {int} Y click location
18278      */
18279     startDrag: function(x, y) { /* override this */ },
18280
18281     /**
18282      * Code that executes immediately before the onDrag event
18283      * @method b4Drag
18284      * @private
18285      */
18286     b4Drag: function(e) { },
18287
18288     /**
18289      * Abstract method called during the onMouseMove event while dragging an
18290      * object.
18291      * @method onDrag
18292      * @param {Event} e the mousemove event
18293      */
18294     onDrag: function(e) { /* override this */ },
18295
18296     /**
18297      * Abstract method called when this element fist begins hovering over
18298      * another DragDrop obj
18299      * @method onDragEnter
18300      * @param {Event} e the mousemove event
18301      * @param {String|DragDrop[]} id In POINT mode, the element
18302      * id this is hovering over.  In INTERSECT mode, an array of one or more
18303      * dragdrop items being hovered over.
18304      */
18305     onDragEnter: function(e, id) { /* override this */ },
18306
18307     /**
18308      * Code that executes immediately before the onDragOver event
18309      * @method b4DragOver
18310      * @private
18311      */
18312     b4DragOver: function(e) { },
18313
18314     /**
18315      * Abstract method called when this element is hovering over another
18316      * DragDrop obj
18317      * @method onDragOver
18318      * @param {Event} e the mousemove event
18319      * @param {String|DragDrop[]} id In POINT mode, the element
18320      * id this is hovering over.  In INTERSECT mode, an array of dd items
18321      * being hovered over.
18322      */
18323     onDragOver: function(e, id) { /* override this */ },
18324
18325     /**
18326      * Code that executes immediately before the onDragOut event
18327      * @method b4DragOut
18328      * @private
18329      */
18330     b4DragOut: function(e) { },
18331
18332     /**
18333      * Abstract method called when we are no longer hovering over an element
18334      * @method onDragOut
18335      * @param {Event} e the mousemove event
18336      * @param {String|DragDrop[]} id In POINT mode, the element
18337      * id this was hovering over.  In INTERSECT mode, an array of dd items
18338      * that the mouse is no longer over.
18339      */
18340     onDragOut: function(e, id) { /* override this */ },
18341
18342     /**
18343      * Code that executes immediately before the onDragDrop event
18344      * @method b4DragDrop
18345      * @private
18346      */
18347     b4DragDrop: function(e) { },
18348
18349     /**
18350      * Abstract method called when this item is dropped on another DragDrop
18351      * obj
18352      * @method onDragDrop
18353      * @param {Event} e the mouseup event
18354      * @param {String|DragDrop[]} id In POINT mode, the element
18355      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18356      * was dropped on.
18357      */
18358     onDragDrop: function(e, id) { /* override this */ },
18359
18360     /**
18361      * Abstract method called when this item is dropped on an area with no
18362      * drop target
18363      * @method onInvalidDrop
18364      * @param {Event} e the mouseup event
18365      */
18366     onInvalidDrop: function(e) { /* override this */ },
18367
18368     /**
18369      * Code that executes immediately before the endDrag event
18370      * @method b4EndDrag
18371      * @private
18372      */
18373     b4EndDrag: function(e) { },
18374
18375     /**
18376      * Fired when we are done dragging the object
18377      * @method endDrag
18378      * @param {Event} e the mouseup event
18379      */
18380     endDrag: function(e) { /* override this */ },
18381
18382     /**
18383      * Code executed immediately before the onMouseDown event
18384      * @method b4MouseDown
18385      * @param {Event} e the mousedown event
18386      * @private
18387      */
18388     b4MouseDown: function(e) {  },
18389
18390     /**
18391      * Event handler that fires when a drag/drop obj gets a mousedown
18392      * @method onMouseDown
18393      * @param {Event} e the mousedown event
18394      */
18395     onMouseDown: function(e) { /* override this */ },
18396
18397     /**
18398      * Event handler that fires when a drag/drop obj gets a mouseup
18399      * @method onMouseUp
18400      * @param {Event} e the mouseup event
18401      */
18402     onMouseUp: function(e) { /* override this */ },
18403
18404     /**
18405      * Override the onAvailable method to do what is needed after the initial
18406      * position was determined.
18407      * @method onAvailable
18408      */
18409     onAvailable: function () {
18410     },
18411
18412     /*
18413      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18414      * @type Object
18415      */
18416     defaultPadding : {left:0, right:0, top:0, bottom:0},
18417
18418     /*
18419      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18420  *
18421  * Usage:
18422  <pre><code>
18423  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18424                 { dragElId: "existingProxyDiv" });
18425  dd.startDrag = function(){
18426      this.constrainTo("parent-id");
18427  };
18428  </code></pre>
18429  * Or you can initalize it using the {@link Roo.Element} object:
18430  <pre><code>
18431  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18432      startDrag : function(){
18433          this.constrainTo("parent-id");
18434      }
18435  });
18436  </code></pre>
18437      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18438      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18439      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18440      * an object containing the sides to pad. For example: {right:10, bottom:10}
18441      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18442      */
18443     constrainTo : function(constrainTo, pad, inContent){
18444         if(typeof pad == "number"){
18445             pad = {left: pad, right:pad, top:pad, bottom:pad};
18446         }
18447         pad = pad || this.defaultPadding;
18448         var b = Roo.get(this.getEl()).getBox();
18449         var ce = Roo.get(constrainTo);
18450         var s = ce.getScroll();
18451         var c, cd = ce.dom;
18452         if(cd == document.body){
18453             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18454         }else{
18455             xy = ce.getXY();
18456             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18457         }
18458
18459
18460         var topSpace = b.y - c.y;
18461         var leftSpace = b.x - c.x;
18462
18463         this.resetConstraints();
18464         this.setXConstraint(leftSpace - (pad.left||0), // left
18465                 c.width - leftSpace - b.width - (pad.right||0) //right
18466         );
18467         this.setYConstraint(topSpace - (pad.top||0), //top
18468                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18469         );
18470     },
18471
18472     /**
18473      * Returns a reference to the linked element
18474      * @method getEl
18475      * @return {HTMLElement} the html element
18476      */
18477     getEl: function() {
18478         if (!this._domRef) {
18479             this._domRef = Roo.getDom(this.id);
18480         }
18481
18482         return this._domRef;
18483     },
18484
18485     /**
18486      * Returns a reference to the actual element to drag.  By default this is
18487      * the same as the html element, but it can be assigned to another
18488      * element. An example of this can be found in Roo.dd.DDProxy
18489      * @method getDragEl
18490      * @return {HTMLElement} the html element
18491      */
18492     getDragEl: function() {
18493         return Roo.getDom(this.dragElId);
18494     },
18495
18496     /**
18497      * Sets up the DragDrop object.  Must be called in the constructor of any
18498      * Roo.dd.DragDrop subclass
18499      * @method init
18500      * @param id the id of the linked element
18501      * @param {String} sGroup the group of related items
18502      * @param {object} config configuration attributes
18503      */
18504     init: function(id, sGroup, config) {
18505         this.initTarget(id, sGroup, config);
18506         if (!Roo.isTouch) {
18507             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18508         }
18509         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18510         // Event.on(this.id, "selectstart", Event.preventDefault);
18511     },
18512
18513     /**
18514      * Initializes Targeting functionality only... the object does not
18515      * get a mousedown handler.
18516      * @method initTarget
18517      * @param id the id of the linked element
18518      * @param {String} sGroup the group of related items
18519      * @param {object} config configuration attributes
18520      */
18521     initTarget: function(id, sGroup, config) {
18522
18523         // configuration attributes
18524         this.config = config || {};
18525
18526         // create a local reference to the drag and drop manager
18527         this.DDM = Roo.dd.DDM;
18528         // initialize the groups array
18529         this.groups = {};
18530
18531         // assume that we have an element reference instead of an id if the
18532         // parameter is not a string
18533         if (typeof id !== "string") {
18534             id = Roo.id(id);
18535         }
18536
18537         // set the id
18538         this.id = id;
18539
18540         // add to an interaction group
18541         this.addToGroup((sGroup) ? sGroup : "default");
18542
18543         // We don't want to register this as the handle with the manager
18544         // so we just set the id rather than calling the setter.
18545         this.handleElId = id;
18546
18547         // the linked element is the element that gets dragged by default
18548         this.setDragElId(id);
18549
18550         // by default, clicked anchors will not start drag operations.
18551         this.invalidHandleTypes = { A: "A" };
18552         this.invalidHandleIds = {};
18553         this.invalidHandleClasses = [];
18554
18555         this.applyConfig();
18556
18557         this.handleOnAvailable();
18558     },
18559
18560     /**
18561      * Applies the configuration parameters that were passed into the constructor.
18562      * This is supposed to happen at each level through the inheritance chain.  So
18563      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18564      * DragDrop in order to get all of the parameters that are available in
18565      * each object.
18566      * @method applyConfig
18567      */
18568     applyConfig: function() {
18569
18570         // configurable properties:
18571         //    padding, isTarget, maintainOffset, primaryButtonOnly
18572         this.padding           = this.config.padding || [0, 0, 0, 0];
18573         this.isTarget          = (this.config.isTarget !== false);
18574         this.maintainOffset    = (this.config.maintainOffset);
18575         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18576
18577     },
18578
18579     /**
18580      * Executed when the linked element is available
18581      * @method handleOnAvailable
18582      * @private
18583      */
18584     handleOnAvailable: function() {
18585         this.available = true;
18586         this.resetConstraints();
18587         this.onAvailable();
18588     },
18589
18590      /**
18591      * Configures the padding for the target zone in px.  Effectively expands
18592      * (or reduces) the virtual object size for targeting calculations.
18593      * Supports css-style shorthand; if only one parameter is passed, all sides
18594      * will have that padding, and if only two are passed, the top and bottom
18595      * will have the first param, the left and right the second.
18596      * @method setPadding
18597      * @param {int} iTop    Top pad
18598      * @param {int} iRight  Right pad
18599      * @param {int} iBot    Bot pad
18600      * @param {int} iLeft   Left pad
18601      */
18602     setPadding: function(iTop, iRight, iBot, iLeft) {
18603         // this.padding = [iLeft, iRight, iTop, iBot];
18604         if (!iRight && 0 !== iRight) {
18605             this.padding = [iTop, iTop, iTop, iTop];
18606         } else if (!iBot && 0 !== iBot) {
18607             this.padding = [iTop, iRight, iTop, iRight];
18608         } else {
18609             this.padding = [iTop, iRight, iBot, iLeft];
18610         }
18611     },
18612
18613     /**
18614      * Stores the initial placement of the linked element.
18615      * @method setInitialPosition
18616      * @param {int} diffX   the X offset, default 0
18617      * @param {int} diffY   the Y offset, default 0
18618      */
18619     setInitPosition: function(diffX, diffY) {
18620         var el = this.getEl();
18621
18622         if (!this.DDM.verifyEl(el)) {
18623             return;
18624         }
18625
18626         var dx = diffX || 0;
18627         var dy = diffY || 0;
18628
18629         var p = Dom.getXY( el );
18630
18631         this.initPageX = p[0] - dx;
18632         this.initPageY = p[1] - dy;
18633
18634         this.lastPageX = p[0];
18635         this.lastPageY = p[1];
18636
18637
18638         this.setStartPosition(p);
18639     },
18640
18641     /**
18642      * Sets the start position of the element.  This is set when the obj
18643      * is initialized, the reset when a drag is started.
18644      * @method setStartPosition
18645      * @param pos current position (from previous lookup)
18646      * @private
18647      */
18648     setStartPosition: function(pos) {
18649         var p = pos || Dom.getXY( this.getEl() );
18650         this.deltaSetXY = null;
18651
18652         this.startPageX = p[0];
18653         this.startPageY = p[1];
18654     },
18655
18656     /**
18657      * Add this instance to a group of related drag/drop objects.  All
18658      * instances belong to at least one group, and can belong to as many
18659      * groups as needed.
18660      * @method addToGroup
18661      * @param sGroup {string} the name of the group
18662      */
18663     addToGroup: function(sGroup) {
18664         this.groups[sGroup] = true;
18665         this.DDM.regDragDrop(this, sGroup);
18666     },
18667
18668     /**
18669      * Remove's this instance from the supplied interaction group
18670      * @method removeFromGroup
18671      * @param {string}  sGroup  The group to drop
18672      */
18673     removeFromGroup: function(sGroup) {
18674         if (this.groups[sGroup]) {
18675             delete this.groups[sGroup];
18676         }
18677
18678         this.DDM.removeDDFromGroup(this, sGroup);
18679     },
18680
18681     /**
18682      * Allows you to specify that an element other than the linked element
18683      * will be moved with the cursor during a drag
18684      * @method setDragElId
18685      * @param id {string} the id of the element that will be used to initiate the drag
18686      */
18687     setDragElId: function(id) {
18688         this.dragElId = id;
18689     },
18690
18691     /**
18692      * Allows you to specify a child of the linked element that should be
18693      * used to initiate the drag operation.  An example of this would be if
18694      * you have a content div with text and links.  Clicking anywhere in the
18695      * content area would normally start the drag operation.  Use this method
18696      * to specify that an element inside of the content div is the element
18697      * that starts the drag operation.
18698      * @method setHandleElId
18699      * @param id {string} the id of the element that will be used to
18700      * initiate the drag.
18701      */
18702     setHandleElId: function(id) {
18703         if (typeof id !== "string") {
18704             id = Roo.id(id);
18705         }
18706         this.handleElId = id;
18707         this.DDM.regHandle(this.id, id);
18708     },
18709
18710     /**
18711      * Allows you to set an element outside of the linked element as a drag
18712      * handle
18713      * @method setOuterHandleElId
18714      * @param id the id of the element that will be used to initiate the drag
18715      */
18716     setOuterHandleElId: function(id) {
18717         if (typeof id !== "string") {
18718             id = Roo.id(id);
18719         }
18720         Event.on(id, "mousedown",
18721                 this.handleMouseDown, this);
18722         this.setHandleElId(id);
18723
18724         this.hasOuterHandles = true;
18725     },
18726
18727     /**
18728      * Remove all drag and drop hooks for this element
18729      * @method unreg
18730      */
18731     unreg: function() {
18732         Event.un(this.id, "mousedown",
18733                 this.handleMouseDown);
18734         Event.un(this.id, "touchstart",
18735                 this.handleMouseDown);
18736         this._domRef = null;
18737         this.DDM._remove(this);
18738     },
18739
18740     destroy : function(){
18741         this.unreg();
18742     },
18743
18744     /**
18745      * Returns true if this instance is locked, or the drag drop mgr is locked
18746      * (meaning that all drag/drop is disabled on the page.)
18747      * @method isLocked
18748      * @return {boolean} true if this obj or all drag/drop is locked, else
18749      * false
18750      */
18751     isLocked: function() {
18752         return (this.DDM.isLocked() || this.locked);
18753     },
18754
18755     /**
18756      * Fired when this object is clicked
18757      * @method handleMouseDown
18758      * @param {Event} e
18759      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18760      * @private
18761      */
18762     handleMouseDown: function(e, oDD){
18763      
18764         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18765             //Roo.log('not touch/ button !=0');
18766             return;
18767         }
18768         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18769             return; // double touch..
18770         }
18771         
18772
18773         if (this.isLocked()) {
18774             //Roo.log('locked');
18775             return;
18776         }
18777
18778         this.DDM.refreshCache(this.groups);
18779 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18780         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18781         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18782             //Roo.log('no outer handes or not over target');
18783                 // do nothing.
18784         } else {
18785 //            Roo.log('check validator');
18786             if (this.clickValidator(e)) {
18787 //                Roo.log('validate success');
18788                 // set the initial element position
18789                 this.setStartPosition();
18790
18791
18792                 this.b4MouseDown(e);
18793                 this.onMouseDown(e);
18794
18795                 this.DDM.handleMouseDown(e, this);
18796
18797                 this.DDM.stopEvent(e);
18798             } else {
18799
18800
18801             }
18802         }
18803     },
18804
18805     clickValidator: function(e) {
18806         var target = e.getTarget();
18807         return ( this.isValidHandleChild(target) &&
18808                     (this.id == this.handleElId ||
18809                         this.DDM.handleWasClicked(target, this.id)) );
18810     },
18811
18812     /**
18813      * Allows you to specify a tag name that should not start a drag operation
18814      * when clicked.  This is designed to facilitate embedding links within a
18815      * drag handle that do something other than start the drag.
18816      * @method addInvalidHandleType
18817      * @param {string} tagName the type of element to exclude
18818      */
18819     addInvalidHandleType: function(tagName) {
18820         var type = tagName.toUpperCase();
18821         this.invalidHandleTypes[type] = type;
18822     },
18823
18824     /**
18825      * Lets you to specify an element id for a child of a drag handle
18826      * that should not initiate a drag
18827      * @method addInvalidHandleId
18828      * @param {string} id the element id of the element you wish to ignore
18829      */
18830     addInvalidHandleId: function(id) {
18831         if (typeof id !== "string") {
18832             id = Roo.id(id);
18833         }
18834         this.invalidHandleIds[id] = id;
18835     },
18836
18837     /**
18838      * Lets you specify a css class of elements that will not initiate a drag
18839      * @method addInvalidHandleClass
18840      * @param {string} cssClass the class of the elements you wish to ignore
18841      */
18842     addInvalidHandleClass: function(cssClass) {
18843         this.invalidHandleClasses.push(cssClass);
18844     },
18845
18846     /**
18847      * Unsets an excluded tag name set by addInvalidHandleType
18848      * @method removeInvalidHandleType
18849      * @param {string} tagName the type of element to unexclude
18850      */
18851     removeInvalidHandleType: function(tagName) {
18852         var type = tagName.toUpperCase();
18853         // this.invalidHandleTypes[type] = null;
18854         delete this.invalidHandleTypes[type];
18855     },
18856
18857     /**
18858      * Unsets an invalid handle id
18859      * @method removeInvalidHandleId
18860      * @param {string} id the id of the element to re-enable
18861      */
18862     removeInvalidHandleId: function(id) {
18863         if (typeof id !== "string") {
18864             id = Roo.id(id);
18865         }
18866         delete this.invalidHandleIds[id];
18867     },
18868
18869     /**
18870      * Unsets an invalid css class
18871      * @method removeInvalidHandleClass
18872      * @param {string} cssClass the class of the element(s) you wish to
18873      * re-enable
18874      */
18875     removeInvalidHandleClass: function(cssClass) {
18876         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18877             if (this.invalidHandleClasses[i] == cssClass) {
18878                 delete this.invalidHandleClasses[i];
18879             }
18880         }
18881     },
18882
18883     /**
18884      * Checks the tag exclusion list to see if this click should be ignored
18885      * @method isValidHandleChild
18886      * @param {HTMLElement} node the HTMLElement to evaluate
18887      * @return {boolean} true if this is a valid tag type, false if not
18888      */
18889     isValidHandleChild: function(node) {
18890
18891         var valid = true;
18892         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18893         var nodeName;
18894         try {
18895             nodeName = node.nodeName.toUpperCase();
18896         } catch(e) {
18897             nodeName = node.nodeName;
18898         }
18899         valid = valid && !this.invalidHandleTypes[nodeName];
18900         valid = valid && !this.invalidHandleIds[node.id];
18901
18902         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18903             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18904         }
18905
18906
18907         return valid;
18908
18909     },
18910
18911     /**
18912      * Create the array of horizontal tick marks if an interval was specified
18913      * in setXConstraint().
18914      * @method setXTicks
18915      * @private
18916      */
18917     setXTicks: function(iStartX, iTickSize) {
18918         this.xTicks = [];
18919         this.xTickSize = iTickSize;
18920
18921         var tickMap = {};
18922
18923         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18924             if (!tickMap[i]) {
18925                 this.xTicks[this.xTicks.length] = i;
18926                 tickMap[i] = true;
18927             }
18928         }
18929
18930         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18931             if (!tickMap[i]) {
18932                 this.xTicks[this.xTicks.length] = i;
18933                 tickMap[i] = true;
18934             }
18935         }
18936
18937         this.xTicks.sort(this.DDM.numericSort) ;
18938     },
18939
18940     /**
18941      * Create the array of vertical tick marks if an interval was specified in
18942      * setYConstraint().
18943      * @method setYTicks
18944      * @private
18945      */
18946     setYTicks: function(iStartY, iTickSize) {
18947         this.yTicks = [];
18948         this.yTickSize = iTickSize;
18949
18950         var tickMap = {};
18951
18952         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18953             if (!tickMap[i]) {
18954                 this.yTicks[this.yTicks.length] = i;
18955                 tickMap[i] = true;
18956             }
18957         }
18958
18959         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18960             if (!tickMap[i]) {
18961                 this.yTicks[this.yTicks.length] = i;
18962                 tickMap[i] = true;
18963             }
18964         }
18965
18966         this.yTicks.sort(this.DDM.numericSort) ;
18967     },
18968
18969     /**
18970      * By default, the element can be dragged any place on the screen.  Use
18971      * this method to limit the horizontal travel of the element.  Pass in
18972      * 0,0 for the parameters if you want to lock the drag to the y axis.
18973      * @method setXConstraint
18974      * @param {int} iLeft the number of pixels the element can move to the left
18975      * @param {int} iRight the number of pixels the element can move to the
18976      * right
18977      * @param {int} iTickSize optional parameter for specifying that the
18978      * element
18979      * should move iTickSize pixels at a time.
18980      */
18981     setXConstraint: function(iLeft, iRight, iTickSize) {
18982         this.leftConstraint = iLeft;
18983         this.rightConstraint = iRight;
18984
18985         this.minX = this.initPageX - iLeft;
18986         this.maxX = this.initPageX + iRight;
18987         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
18988
18989         this.constrainX = true;
18990     },
18991
18992     /**
18993      * Clears any constraints applied to this instance.  Also clears ticks
18994      * since they can't exist independent of a constraint at this time.
18995      * @method clearConstraints
18996      */
18997     clearConstraints: function() {
18998         this.constrainX = false;
18999         this.constrainY = false;
19000         this.clearTicks();
19001     },
19002
19003     /**
19004      * Clears any tick interval defined for this instance
19005      * @method clearTicks
19006      */
19007     clearTicks: function() {
19008         this.xTicks = null;
19009         this.yTicks = null;
19010         this.xTickSize = 0;
19011         this.yTickSize = 0;
19012     },
19013
19014     /**
19015      * By default, the element can be dragged any place on the screen.  Set
19016      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19017      * parameters if you want to lock the drag to the x axis.
19018      * @method setYConstraint
19019      * @param {int} iUp the number of pixels the element can move up
19020      * @param {int} iDown the number of pixels the element can move down
19021      * @param {int} iTickSize optional parameter for specifying that the
19022      * element should move iTickSize pixels at a time.
19023      */
19024     setYConstraint: function(iUp, iDown, iTickSize) {
19025         this.topConstraint = iUp;
19026         this.bottomConstraint = iDown;
19027
19028         this.minY = this.initPageY - iUp;
19029         this.maxY = this.initPageY + iDown;
19030         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19031
19032         this.constrainY = true;
19033
19034     },
19035
19036     /**
19037      * resetConstraints must be called if you manually reposition a dd element.
19038      * @method resetConstraints
19039      * @param {boolean} maintainOffset
19040      */
19041     resetConstraints: function() {
19042
19043
19044         // Maintain offsets if necessary
19045         if (this.initPageX || this.initPageX === 0) {
19046             // figure out how much this thing has moved
19047             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19048             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19049
19050             this.setInitPosition(dx, dy);
19051
19052         // This is the first time we have detected the element's position
19053         } else {
19054             this.setInitPosition();
19055         }
19056
19057         if (this.constrainX) {
19058             this.setXConstraint( this.leftConstraint,
19059                                  this.rightConstraint,
19060                                  this.xTickSize        );
19061         }
19062
19063         if (this.constrainY) {
19064             this.setYConstraint( this.topConstraint,
19065                                  this.bottomConstraint,
19066                                  this.yTickSize         );
19067         }
19068     },
19069
19070     /**
19071      * Normally the drag element is moved pixel by pixel, but we can specify
19072      * that it move a number of pixels at a time.  This method resolves the
19073      * location when we have it set up like this.
19074      * @method getTick
19075      * @param {int} val where we want to place the object
19076      * @param {int[]} tickArray sorted array of valid points
19077      * @return {int} the closest tick
19078      * @private
19079      */
19080     getTick: function(val, tickArray) {
19081
19082         if (!tickArray) {
19083             // If tick interval is not defined, it is effectively 1 pixel,
19084             // so we return the value passed to us.
19085             return val;
19086         } else if (tickArray[0] >= val) {
19087             // The value is lower than the first tick, so we return the first
19088             // tick.
19089             return tickArray[0];
19090         } else {
19091             for (var i=0, len=tickArray.length; i<len; ++i) {
19092                 var next = i + 1;
19093                 if (tickArray[next] && tickArray[next] >= val) {
19094                     var diff1 = val - tickArray[i];
19095                     var diff2 = tickArray[next] - val;
19096                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19097                 }
19098             }
19099
19100             // The value is larger than the last tick, so we return the last
19101             // tick.
19102             return tickArray[tickArray.length - 1];
19103         }
19104     },
19105
19106     /**
19107      * toString method
19108      * @method toString
19109      * @return {string} string representation of the dd obj
19110      */
19111     toString: function() {
19112         return ("DragDrop " + this.id);
19113     }
19114
19115 });
19116
19117 })();
19118 /*
19119  * Based on:
19120  * Ext JS Library 1.1.1
19121  * Copyright(c) 2006-2007, Ext JS, LLC.
19122  *
19123  * Originally Released Under LGPL - original licence link has changed is not relivant.
19124  *
19125  * Fork - LGPL
19126  * <script type="text/javascript">
19127  */
19128
19129
19130 /**
19131  * The drag and drop utility provides a framework for building drag and drop
19132  * applications.  In addition to enabling drag and drop for specific elements,
19133  * the drag and drop elements are tracked by the manager class, and the
19134  * interactions between the various elements are tracked during the drag and
19135  * the implementing code is notified about these important moments.
19136  */
19137
19138 // Only load the library once.  Rewriting the manager class would orphan
19139 // existing drag and drop instances.
19140 if (!Roo.dd.DragDropMgr) {
19141
19142 /**
19143  * @class Roo.dd.DragDropMgr
19144  * DragDropMgr is a singleton that tracks the element interaction for
19145  * all DragDrop items in the window.  Generally, you will not call
19146  * this class directly, but it does have helper methods that could
19147  * be useful in your DragDrop implementations.
19148  * @singleton
19149  */
19150 Roo.dd.DragDropMgr = function() {
19151
19152     var Event = Roo.EventManager;
19153
19154     return {
19155
19156         /**
19157          * Two dimensional Array of registered DragDrop objects.  The first
19158          * dimension is the DragDrop item group, the second the DragDrop
19159          * object.
19160          * @property ids
19161          * @type {string: string}
19162          * @private
19163          * @static
19164          */
19165         ids: {},
19166
19167         /**
19168          * Array of element ids defined as drag handles.  Used to determine
19169          * if the element that generated the mousedown event is actually the
19170          * handle and not the html element itself.
19171          * @property handleIds
19172          * @type {string: string}
19173          * @private
19174          * @static
19175          */
19176         handleIds: {},
19177
19178         /**
19179          * the DragDrop object that is currently being dragged
19180          * @property dragCurrent
19181          * @type DragDrop
19182          * @private
19183          * @static
19184          **/
19185         dragCurrent: null,
19186
19187         /**
19188          * the DragDrop object(s) that are being hovered over
19189          * @property dragOvers
19190          * @type Array
19191          * @private
19192          * @static
19193          */
19194         dragOvers: {},
19195
19196         /**
19197          * the X distance between the cursor and the object being dragged
19198          * @property deltaX
19199          * @type int
19200          * @private
19201          * @static
19202          */
19203         deltaX: 0,
19204
19205         /**
19206          * the Y distance between the cursor and the object being dragged
19207          * @property deltaY
19208          * @type int
19209          * @private
19210          * @static
19211          */
19212         deltaY: 0,
19213
19214         /**
19215          * Flag to determine if we should prevent the default behavior of the
19216          * events we define. By default this is true, but this can be set to
19217          * false if you need the default behavior (not recommended)
19218          * @property preventDefault
19219          * @type boolean
19220          * @static
19221          */
19222         preventDefault: true,
19223
19224         /**
19225          * Flag to determine if we should stop the propagation of the events
19226          * we generate. This is true by default but you may want to set it to
19227          * false if the html element contains other features that require the
19228          * mouse click.
19229          * @property stopPropagation
19230          * @type boolean
19231          * @static
19232          */
19233         stopPropagation: true,
19234
19235         /**
19236          * Internal flag that is set to true when drag and drop has been
19237          * intialized
19238          * @property initialized
19239          * @private
19240          * @static
19241          */
19242         initalized: false,
19243
19244         /**
19245          * All drag and drop can be disabled.
19246          * @property locked
19247          * @private
19248          * @static
19249          */
19250         locked: false,
19251
19252         /**
19253          * Called the first time an element is registered.
19254          * @method init
19255          * @private
19256          * @static
19257          */
19258         init: function() {
19259             this.initialized = true;
19260         },
19261
19262         /**
19263          * In point mode, drag and drop interaction is defined by the
19264          * location of the cursor during the drag/drop
19265          * @property POINT
19266          * @type int
19267          * @static
19268          */
19269         POINT: 0,
19270
19271         /**
19272          * In intersect mode, drag and drop interactio nis defined by the
19273          * overlap of two or more drag and drop objects.
19274          * @property INTERSECT
19275          * @type int
19276          * @static
19277          */
19278         INTERSECT: 1,
19279
19280         /**
19281          * The current drag and drop mode.  Default: POINT
19282          * @property mode
19283          * @type int
19284          * @static
19285          */
19286         mode: 0,
19287
19288         /**
19289          * Runs method on all drag and drop objects
19290          * @method _execOnAll
19291          * @private
19292          * @static
19293          */
19294         _execOnAll: function(sMethod, args) {
19295             for (var i in this.ids) {
19296                 for (var j in this.ids[i]) {
19297                     var oDD = this.ids[i][j];
19298                     if (! this.isTypeOfDD(oDD)) {
19299                         continue;
19300                     }
19301                     oDD[sMethod].apply(oDD, args);
19302                 }
19303             }
19304         },
19305
19306         /**
19307          * Drag and drop initialization.  Sets up the global event handlers
19308          * @method _onLoad
19309          * @private
19310          * @static
19311          */
19312         _onLoad: function() {
19313
19314             this.init();
19315
19316             if (!Roo.isTouch) {
19317                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19318                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19319             }
19320             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19321             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19322             
19323             Event.on(window,   "unload",    this._onUnload, this, true);
19324             Event.on(window,   "resize",    this._onResize, this, true);
19325             // Event.on(window,   "mouseout",    this._test);
19326
19327         },
19328
19329         /**
19330          * Reset constraints on all drag and drop objs
19331          * @method _onResize
19332          * @private
19333          * @static
19334          */
19335         _onResize: function(e) {
19336             this._execOnAll("resetConstraints", []);
19337         },
19338
19339         /**
19340          * Lock all drag and drop functionality
19341          * @method lock
19342          * @static
19343          */
19344         lock: function() { this.locked = true; },
19345
19346         /**
19347          * Unlock all drag and drop functionality
19348          * @method unlock
19349          * @static
19350          */
19351         unlock: function() { this.locked = false; },
19352
19353         /**
19354          * Is drag and drop locked?
19355          * @method isLocked
19356          * @return {boolean} True if drag and drop is locked, false otherwise.
19357          * @static
19358          */
19359         isLocked: function() { return this.locked; },
19360
19361         /**
19362          * Location cache that is set for all drag drop objects when a drag is
19363          * initiated, cleared when the drag is finished.
19364          * @property locationCache
19365          * @private
19366          * @static
19367          */
19368         locationCache: {},
19369
19370         /**
19371          * Set useCache to false if you want to force object the lookup of each
19372          * drag and drop linked element constantly during a drag.
19373          * @property useCache
19374          * @type boolean
19375          * @static
19376          */
19377         useCache: true,
19378
19379         /**
19380          * The number of pixels that the mouse needs to move after the
19381          * mousedown before the drag is initiated.  Default=3;
19382          * @property clickPixelThresh
19383          * @type int
19384          * @static
19385          */
19386         clickPixelThresh: 3,
19387
19388         /**
19389          * The number of milliseconds after the mousedown event to initiate the
19390          * drag if we don't get a mouseup event. Default=1000
19391          * @property clickTimeThresh
19392          * @type int
19393          * @static
19394          */
19395         clickTimeThresh: 350,
19396
19397         /**
19398          * Flag that indicates that either the drag pixel threshold or the
19399          * mousdown time threshold has been met
19400          * @property dragThreshMet
19401          * @type boolean
19402          * @private
19403          * @static
19404          */
19405         dragThreshMet: false,
19406
19407         /**
19408          * Timeout used for the click time threshold
19409          * @property clickTimeout
19410          * @type Object
19411          * @private
19412          * @static
19413          */
19414         clickTimeout: null,
19415
19416         /**
19417          * The X position of the mousedown event stored for later use when a
19418          * drag threshold is met.
19419          * @property startX
19420          * @type int
19421          * @private
19422          * @static
19423          */
19424         startX: 0,
19425
19426         /**
19427          * The Y position of the mousedown event stored for later use when a
19428          * drag threshold is met.
19429          * @property startY
19430          * @type int
19431          * @private
19432          * @static
19433          */
19434         startY: 0,
19435
19436         /**
19437          * Each DragDrop instance must be registered with the DragDropMgr.
19438          * This is executed in DragDrop.init()
19439          * @method regDragDrop
19440          * @param {DragDrop} oDD the DragDrop object to register
19441          * @param {String} sGroup the name of the group this element belongs to
19442          * @static
19443          */
19444         regDragDrop: function(oDD, sGroup) {
19445             if (!this.initialized) { this.init(); }
19446
19447             if (!this.ids[sGroup]) {
19448                 this.ids[sGroup] = {};
19449             }
19450             this.ids[sGroup][oDD.id] = oDD;
19451         },
19452
19453         /**
19454          * Removes the supplied dd instance from the supplied group. Executed
19455          * by DragDrop.removeFromGroup, so don't call this function directly.
19456          * @method removeDDFromGroup
19457          * @private
19458          * @static
19459          */
19460         removeDDFromGroup: function(oDD, sGroup) {
19461             if (!this.ids[sGroup]) {
19462                 this.ids[sGroup] = {};
19463             }
19464
19465             var obj = this.ids[sGroup];
19466             if (obj && obj[oDD.id]) {
19467                 delete obj[oDD.id];
19468             }
19469         },
19470
19471         /**
19472          * Unregisters a drag and drop item.  This is executed in
19473          * DragDrop.unreg, use that method instead of calling this directly.
19474          * @method _remove
19475          * @private
19476          * @static
19477          */
19478         _remove: function(oDD) {
19479             for (var g in oDD.groups) {
19480                 if (g && this.ids[g][oDD.id]) {
19481                     delete this.ids[g][oDD.id];
19482                 }
19483             }
19484             delete this.handleIds[oDD.id];
19485         },
19486
19487         /**
19488          * Each DragDrop handle element must be registered.  This is done
19489          * automatically when executing DragDrop.setHandleElId()
19490          * @method regHandle
19491          * @param {String} sDDId the DragDrop id this element is a handle for
19492          * @param {String} sHandleId the id of the element that is the drag
19493          * handle
19494          * @static
19495          */
19496         regHandle: function(sDDId, sHandleId) {
19497             if (!this.handleIds[sDDId]) {
19498                 this.handleIds[sDDId] = {};
19499             }
19500             this.handleIds[sDDId][sHandleId] = sHandleId;
19501         },
19502
19503         /**
19504          * Utility function to determine if a given element has been
19505          * registered as a drag drop item.
19506          * @method isDragDrop
19507          * @param {String} id the element id to check
19508          * @return {boolean} true if this element is a DragDrop item,
19509          * false otherwise
19510          * @static
19511          */
19512         isDragDrop: function(id) {
19513             return ( this.getDDById(id) ) ? true : false;
19514         },
19515
19516         /**
19517          * Returns the drag and drop instances that are in all groups the
19518          * passed in instance belongs to.
19519          * @method getRelated
19520          * @param {DragDrop} p_oDD the obj to get related data for
19521          * @param {boolean} bTargetsOnly if true, only return targetable objs
19522          * @return {DragDrop[]} the related instances
19523          * @static
19524          */
19525         getRelated: function(p_oDD, bTargetsOnly) {
19526             var oDDs = [];
19527             for (var i in p_oDD.groups) {
19528                 for (j in this.ids[i]) {
19529                     var dd = this.ids[i][j];
19530                     if (! this.isTypeOfDD(dd)) {
19531                         continue;
19532                     }
19533                     if (!bTargetsOnly || dd.isTarget) {
19534                         oDDs[oDDs.length] = dd;
19535                     }
19536                 }
19537             }
19538
19539             return oDDs;
19540         },
19541
19542         /**
19543          * Returns true if the specified dd target is a legal target for
19544          * the specifice drag obj
19545          * @method isLegalTarget
19546          * @param {DragDrop} the drag obj
19547          * @param {DragDrop} the target
19548          * @return {boolean} true if the target is a legal target for the
19549          * dd obj
19550          * @static
19551          */
19552         isLegalTarget: function (oDD, oTargetDD) {
19553             var targets = this.getRelated(oDD, true);
19554             for (var i=0, len=targets.length;i<len;++i) {
19555                 if (targets[i].id == oTargetDD.id) {
19556                     return true;
19557                 }
19558             }
19559
19560             return false;
19561         },
19562
19563         /**
19564          * My goal is to be able to transparently determine if an object is
19565          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19566          * returns "object", oDD.constructor.toString() always returns
19567          * "DragDrop" and not the name of the subclass.  So for now it just
19568          * evaluates a well-known variable in DragDrop.
19569          * @method isTypeOfDD
19570          * @param {Object} the object to evaluate
19571          * @return {boolean} true if typeof oDD = DragDrop
19572          * @static
19573          */
19574         isTypeOfDD: function (oDD) {
19575             return (oDD && oDD.__ygDragDrop);
19576         },
19577
19578         /**
19579          * Utility function to determine if a given element has been
19580          * registered as a drag drop handle for the given Drag Drop object.
19581          * @method isHandle
19582          * @param {String} id the element id to check
19583          * @return {boolean} true if this element is a DragDrop handle, false
19584          * otherwise
19585          * @static
19586          */
19587         isHandle: function(sDDId, sHandleId) {
19588             return ( this.handleIds[sDDId] &&
19589                             this.handleIds[sDDId][sHandleId] );
19590         },
19591
19592         /**
19593          * Returns the DragDrop instance for a given id
19594          * @method getDDById
19595          * @param {String} id the id of the DragDrop object
19596          * @return {DragDrop} the drag drop object, null if it is not found
19597          * @static
19598          */
19599         getDDById: function(id) {
19600             for (var i in this.ids) {
19601                 if (this.ids[i][id]) {
19602                     return this.ids[i][id];
19603                 }
19604             }
19605             return null;
19606         },
19607
19608         /**
19609          * Fired after a registered DragDrop object gets the mousedown event.
19610          * Sets up the events required to track the object being dragged
19611          * @method handleMouseDown
19612          * @param {Event} e the event
19613          * @param oDD the DragDrop object being dragged
19614          * @private
19615          * @static
19616          */
19617         handleMouseDown: function(e, oDD) {
19618             if(Roo.QuickTips){
19619                 Roo.QuickTips.disable();
19620             }
19621             this.currentTarget = e.getTarget();
19622
19623             this.dragCurrent = oDD;
19624
19625             var el = oDD.getEl();
19626
19627             // track start position
19628             this.startX = e.getPageX();
19629             this.startY = e.getPageY();
19630
19631             this.deltaX = this.startX - el.offsetLeft;
19632             this.deltaY = this.startY - el.offsetTop;
19633
19634             this.dragThreshMet = false;
19635
19636             this.clickTimeout = setTimeout(
19637                     function() {
19638                         var DDM = Roo.dd.DDM;
19639                         DDM.startDrag(DDM.startX, DDM.startY);
19640                     },
19641                     this.clickTimeThresh );
19642         },
19643
19644         /**
19645          * Fired when either the drag pixel threshol or the mousedown hold
19646          * time threshold has been met.
19647          * @method startDrag
19648          * @param x {int} the X position of the original mousedown
19649          * @param y {int} the Y position of the original mousedown
19650          * @static
19651          */
19652         startDrag: function(x, y) {
19653             clearTimeout(this.clickTimeout);
19654             if (this.dragCurrent) {
19655                 this.dragCurrent.b4StartDrag(x, y);
19656                 this.dragCurrent.startDrag(x, y);
19657             }
19658             this.dragThreshMet = true;
19659         },
19660
19661         /**
19662          * Internal function to handle the mouseup event.  Will be invoked
19663          * from the context of the document.
19664          * @method handleMouseUp
19665          * @param {Event} e the event
19666          * @private
19667          * @static
19668          */
19669         handleMouseUp: function(e) {
19670
19671             if(Roo.QuickTips){
19672                 Roo.QuickTips.enable();
19673             }
19674             if (! this.dragCurrent) {
19675                 return;
19676             }
19677
19678             clearTimeout(this.clickTimeout);
19679
19680             if (this.dragThreshMet) {
19681                 this.fireEvents(e, true);
19682             } else {
19683             }
19684
19685             this.stopDrag(e);
19686
19687             this.stopEvent(e);
19688         },
19689
19690         /**
19691          * Utility to stop event propagation and event default, if these
19692          * features are turned on.
19693          * @method stopEvent
19694          * @param {Event} e the event as returned by this.getEvent()
19695          * @static
19696          */
19697         stopEvent: function(e){
19698             if(this.stopPropagation) {
19699                 e.stopPropagation();
19700             }
19701
19702             if (this.preventDefault) {
19703                 e.preventDefault();
19704             }
19705         },
19706
19707         /**
19708          * Internal function to clean up event handlers after the drag
19709          * operation is complete
19710          * @method stopDrag
19711          * @param {Event} e the event
19712          * @private
19713          * @static
19714          */
19715         stopDrag: function(e) {
19716             // Fire the drag end event for the item that was dragged
19717             if (this.dragCurrent) {
19718                 if (this.dragThreshMet) {
19719                     this.dragCurrent.b4EndDrag(e);
19720                     this.dragCurrent.endDrag(e);
19721                 }
19722
19723                 this.dragCurrent.onMouseUp(e);
19724             }
19725
19726             this.dragCurrent = null;
19727             this.dragOvers = {};
19728         },
19729
19730         /**
19731          * Internal function to handle the mousemove event.  Will be invoked
19732          * from the context of the html element.
19733          *
19734          * @TODO figure out what we can do about mouse events lost when the
19735          * user drags objects beyond the window boundary.  Currently we can
19736          * detect this in internet explorer by verifying that the mouse is
19737          * down during the mousemove event.  Firefox doesn't give us the
19738          * button state on the mousemove event.
19739          * @method handleMouseMove
19740          * @param {Event} e the event
19741          * @private
19742          * @static
19743          */
19744         handleMouseMove: function(e) {
19745             if (! this.dragCurrent) {
19746                 return true;
19747             }
19748
19749             // var button = e.which || e.button;
19750
19751             // check for IE mouseup outside of page boundary
19752             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19753                 this.stopEvent(e);
19754                 return this.handleMouseUp(e);
19755             }
19756
19757             if (!this.dragThreshMet) {
19758                 var diffX = Math.abs(this.startX - e.getPageX());
19759                 var diffY = Math.abs(this.startY - e.getPageY());
19760                 if (diffX > this.clickPixelThresh ||
19761                             diffY > this.clickPixelThresh) {
19762                     this.startDrag(this.startX, this.startY);
19763                 }
19764             }
19765
19766             if (this.dragThreshMet) {
19767                 this.dragCurrent.b4Drag(e);
19768                 this.dragCurrent.onDrag(e);
19769                 if(!this.dragCurrent.moveOnly){
19770                     this.fireEvents(e, false);
19771                 }
19772             }
19773
19774             this.stopEvent(e);
19775
19776             return true;
19777         },
19778
19779         /**
19780          * Iterates over all of the DragDrop elements to find ones we are
19781          * hovering over or dropping on
19782          * @method fireEvents
19783          * @param {Event} e the event
19784          * @param {boolean} isDrop is this a drop op or a mouseover op?
19785          * @private
19786          * @static
19787          */
19788         fireEvents: function(e, isDrop) {
19789             var dc = this.dragCurrent;
19790
19791             // If the user did the mouse up outside of the window, we could
19792             // get here even though we have ended the drag.
19793             if (!dc || dc.isLocked()) {
19794                 return;
19795             }
19796
19797             var pt = e.getPoint();
19798
19799             // cache the previous dragOver array
19800             var oldOvers = [];
19801
19802             var outEvts   = [];
19803             var overEvts  = [];
19804             var dropEvts  = [];
19805             var enterEvts = [];
19806
19807             // Check to see if the object(s) we were hovering over is no longer
19808             // being hovered over so we can fire the onDragOut event
19809             for (var i in this.dragOvers) {
19810
19811                 var ddo = this.dragOvers[i];
19812
19813                 if (! this.isTypeOfDD(ddo)) {
19814                     continue;
19815                 }
19816
19817                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19818                     outEvts.push( ddo );
19819                 }
19820
19821                 oldOvers[i] = true;
19822                 delete this.dragOvers[i];
19823             }
19824
19825             for (var sGroup in dc.groups) {
19826
19827                 if ("string" != typeof sGroup) {
19828                     continue;
19829                 }
19830
19831                 for (i in this.ids[sGroup]) {
19832                     var oDD = this.ids[sGroup][i];
19833                     if (! this.isTypeOfDD(oDD)) {
19834                         continue;
19835                     }
19836
19837                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19838                         if (this.isOverTarget(pt, oDD, this.mode)) {
19839                             // look for drop interactions
19840                             if (isDrop) {
19841                                 dropEvts.push( oDD );
19842                             // look for drag enter and drag over interactions
19843                             } else {
19844
19845                                 // initial drag over: dragEnter fires
19846                                 if (!oldOvers[oDD.id]) {
19847                                     enterEvts.push( oDD );
19848                                 // subsequent drag overs: dragOver fires
19849                                 } else {
19850                                     overEvts.push( oDD );
19851                                 }
19852
19853                                 this.dragOvers[oDD.id] = oDD;
19854                             }
19855                         }
19856                     }
19857                 }
19858             }
19859
19860             if (this.mode) {
19861                 if (outEvts.length) {
19862                     dc.b4DragOut(e, outEvts);
19863                     dc.onDragOut(e, outEvts);
19864                 }
19865
19866                 if (enterEvts.length) {
19867                     dc.onDragEnter(e, enterEvts);
19868                 }
19869
19870                 if (overEvts.length) {
19871                     dc.b4DragOver(e, overEvts);
19872                     dc.onDragOver(e, overEvts);
19873                 }
19874
19875                 if (dropEvts.length) {
19876                     dc.b4DragDrop(e, dropEvts);
19877                     dc.onDragDrop(e, dropEvts);
19878                 }
19879
19880             } else {
19881                 // fire dragout events
19882                 var len = 0;
19883                 for (i=0, len=outEvts.length; i<len; ++i) {
19884                     dc.b4DragOut(e, outEvts[i].id);
19885                     dc.onDragOut(e, outEvts[i].id);
19886                 }
19887
19888                 // fire enter events
19889                 for (i=0,len=enterEvts.length; i<len; ++i) {
19890                     // dc.b4DragEnter(e, oDD.id);
19891                     dc.onDragEnter(e, enterEvts[i].id);
19892                 }
19893
19894                 // fire over events
19895                 for (i=0,len=overEvts.length; i<len; ++i) {
19896                     dc.b4DragOver(e, overEvts[i].id);
19897                     dc.onDragOver(e, overEvts[i].id);
19898                 }
19899
19900                 // fire drop events
19901                 for (i=0, len=dropEvts.length; i<len; ++i) {
19902                     dc.b4DragDrop(e, dropEvts[i].id);
19903                     dc.onDragDrop(e, dropEvts[i].id);
19904                 }
19905
19906             }
19907
19908             // notify about a drop that did not find a target
19909             if (isDrop && !dropEvts.length) {
19910                 dc.onInvalidDrop(e);
19911             }
19912
19913         },
19914
19915         /**
19916          * Helper function for getting the best match from the list of drag
19917          * and drop objects returned by the drag and drop events when we are
19918          * in INTERSECT mode.  It returns either the first object that the
19919          * cursor is over, or the object that has the greatest overlap with
19920          * the dragged element.
19921          * @method getBestMatch
19922          * @param  {DragDrop[]} dds The array of drag and drop objects
19923          * targeted
19924          * @return {DragDrop}       The best single match
19925          * @static
19926          */
19927         getBestMatch: function(dds) {
19928             var winner = null;
19929             // Return null if the input is not what we expect
19930             //if (!dds || !dds.length || dds.length == 0) {
19931                // winner = null;
19932             // If there is only one item, it wins
19933             //} else if (dds.length == 1) {
19934
19935             var len = dds.length;
19936
19937             if (len == 1) {
19938                 winner = dds[0];
19939             } else {
19940                 // Loop through the targeted items
19941                 for (var i=0; i<len; ++i) {
19942                     var dd = dds[i];
19943                     // If the cursor is over the object, it wins.  If the
19944                     // cursor is over multiple matches, the first one we come
19945                     // to wins.
19946                     if (dd.cursorIsOver) {
19947                         winner = dd;
19948                         break;
19949                     // Otherwise the object with the most overlap wins
19950                     } else {
19951                         if (!winner ||
19952                             winner.overlap.getArea() < dd.overlap.getArea()) {
19953                             winner = dd;
19954                         }
19955                     }
19956                 }
19957             }
19958
19959             return winner;
19960         },
19961
19962         /**
19963          * Refreshes the cache of the top-left and bottom-right points of the
19964          * drag and drop objects in the specified group(s).  This is in the
19965          * format that is stored in the drag and drop instance, so typical
19966          * usage is:
19967          * <code>
19968          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19969          * </code>
19970          * Alternatively:
19971          * <code>
19972          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19973          * </code>
19974          * @TODO this really should be an indexed array.  Alternatively this
19975          * method could accept both.
19976          * @method refreshCache
19977          * @param {Object} groups an associative array of groups to refresh
19978          * @static
19979          */
19980         refreshCache: function(groups) {
19981             for (var sGroup in groups) {
19982                 if ("string" != typeof sGroup) {
19983                     continue;
19984                 }
19985                 for (var i in this.ids[sGroup]) {
19986                     var oDD = this.ids[sGroup][i];
19987
19988                     if (this.isTypeOfDD(oDD)) {
19989                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
19990                         var loc = this.getLocation(oDD);
19991                         if (loc) {
19992                             this.locationCache[oDD.id] = loc;
19993                         } else {
19994                             delete this.locationCache[oDD.id];
19995                             // this will unregister the drag and drop object if
19996                             // the element is not in a usable state
19997                             // oDD.unreg();
19998                         }
19999                     }
20000                 }
20001             }
20002         },
20003
20004         /**
20005          * This checks to make sure an element exists and is in the DOM.  The
20006          * main purpose is to handle cases where innerHTML is used to remove
20007          * drag and drop objects from the DOM.  IE provides an 'unspecified
20008          * error' when trying to access the offsetParent of such an element
20009          * @method verifyEl
20010          * @param {HTMLElement} el the element to check
20011          * @return {boolean} true if the element looks usable
20012          * @static
20013          */
20014         verifyEl: function(el) {
20015             if (el) {
20016                 var parent;
20017                 if(Roo.isIE){
20018                     try{
20019                         parent = el.offsetParent;
20020                     }catch(e){}
20021                 }else{
20022                     parent = el.offsetParent;
20023                 }
20024                 if (parent) {
20025                     return true;
20026                 }
20027             }
20028
20029             return false;
20030         },
20031
20032         /**
20033          * Returns a Region object containing the drag and drop element's position
20034          * and size, including the padding configured for it
20035          * @method getLocation
20036          * @param {DragDrop} oDD the drag and drop object to get the
20037          *                       location for
20038          * @return {Roo.lib.Region} a Region object representing the total area
20039          *                             the element occupies, including any padding
20040          *                             the instance is configured for.
20041          * @static
20042          */
20043         getLocation: function(oDD) {
20044             if (! this.isTypeOfDD(oDD)) {
20045                 return null;
20046             }
20047
20048             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20049
20050             try {
20051                 pos= Roo.lib.Dom.getXY(el);
20052             } catch (e) { }
20053
20054             if (!pos) {
20055                 return null;
20056             }
20057
20058             x1 = pos[0];
20059             x2 = x1 + el.offsetWidth;
20060             y1 = pos[1];
20061             y2 = y1 + el.offsetHeight;
20062
20063             t = y1 - oDD.padding[0];
20064             r = x2 + oDD.padding[1];
20065             b = y2 + oDD.padding[2];
20066             l = x1 - oDD.padding[3];
20067
20068             return new Roo.lib.Region( t, r, b, l );
20069         },
20070
20071         /**
20072          * Checks the cursor location to see if it over the target
20073          * @method isOverTarget
20074          * @param {Roo.lib.Point} pt The point to evaluate
20075          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20076          * @return {boolean} true if the mouse is over the target
20077          * @private
20078          * @static
20079          */
20080         isOverTarget: function(pt, oTarget, intersect) {
20081             // use cache if available
20082             var loc = this.locationCache[oTarget.id];
20083             if (!loc || !this.useCache) {
20084                 loc = this.getLocation(oTarget);
20085                 this.locationCache[oTarget.id] = loc;
20086
20087             }
20088
20089             if (!loc) {
20090                 return false;
20091             }
20092
20093             oTarget.cursorIsOver = loc.contains( pt );
20094
20095             // DragDrop is using this as a sanity check for the initial mousedown
20096             // in this case we are done.  In POINT mode, if the drag obj has no
20097             // contraints, we are also done. Otherwise we need to evaluate the
20098             // location of the target as related to the actual location of the
20099             // dragged element.
20100             var dc = this.dragCurrent;
20101             if (!dc || !dc.getTargetCoord ||
20102                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20103                 return oTarget.cursorIsOver;
20104             }
20105
20106             oTarget.overlap = null;
20107
20108             // Get the current location of the drag element, this is the
20109             // location of the mouse event less the delta that represents
20110             // where the original mousedown happened on the element.  We
20111             // need to consider constraints and ticks as well.
20112             var pos = dc.getTargetCoord(pt.x, pt.y);
20113
20114             var el = dc.getDragEl();
20115             var curRegion = new Roo.lib.Region( pos.y,
20116                                                    pos.x + el.offsetWidth,
20117                                                    pos.y + el.offsetHeight,
20118                                                    pos.x );
20119
20120             var overlap = curRegion.intersect(loc);
20121
20122             if (overlap) {
20123                 oTarget.overlap = overlap;
20124                 return (intersect) ? true : oTarget.cursorIsOver;
20125             } else {
20126                 return false;
20127             }
20128         },
20129
20130         /**
20131          * unload event handler
20132          * @method _onUnload
20133          * @private
20134          * @static
20135          */
20136         _onUnload: function(e, me) {
20137             Roo.dd.DragDropMgr.unregAll();
20138         },
20139
20140         /**
20141          * Cleans up the drag and drop events and objects.
20142          * @method unregAll
20143          * @private
20144          * @static
20145          */
20146         unregAll: function() {
20147
20148             if (this.dragCurrent) {
20149                 this.stopDrag();
20150                 this.dragCurrent = null;
20151             }
20152
20153             this._execOnAll("unreg", []);
20154
20155             for (i in this.elementCache) {
20156                 delete this.elementCache[i];
20157             }
20158
20159             this.elementCache = {};
20160             this.ids = {};
20161         },
20162
20163         /**
20164          * A cache of DOM elements
20165          * @property elementCache
20166          * @private
20167          * @static
20168          */
20169         elementCache: {},
20170
20171         /**
20172          * Get the wrapper for the DOM element specified
20173          * @method getElWrapper
20174          * @param {String} id the id of the element to get
20175          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20176          * @private
20177          * @deprecated This wrapper isn't that useful
20178          * @static
20179          */
20180         getElWrapper: function(id) {
20181             var oWrapper = this.elementCache[id];
20182             if (!oWrapper || !oWrapper.el) {
20183                 oWrapper = this.elementCache[id] =
20184                     new this.ElementWrapper(Roo.getDom(id));
20185             }
20186             return oWrapper;
20187         },
20188
20189         /**
20190          * Returns the actual DOM element
20191          * @method getElement
20192          * @param {String} id the id of the elment to get
20193          * @return {Object} The element
20194          * @deprecated use Roo.getDom instead
20195          * @static
20196          */
20197         getElement: function(id) {
20198             return Roo.getDom(id);
20199         },
20200
20201         /**
20202          * Returns the style property for the DOM element (i.e.,
20203          * document.getElById(id).style)
20204          * @method getCss
20205          * @param {String} id the id of the elment to get
20206          * @return {Object} The style property of the element
20207          * @deprecated use Roo.getDom instead
20208          * @static
20209          */
20210         getCss: function(id) {
20211             var el = Roo.getDom(id);
20212             return (el) ? el.style : null;
20213         },
20214
20215         /**
20216          * Inner class for cached elements
20217          * @class DragDropMgr.ElementWrapper
20218          * @for DragDropMgr
20219          * @private
20220          * @deprecated
20221          */
20222         ElementWrapper: function(el) {
20223                 /**
20224                  * The element
20225                  * @property el
20226                  */
20227                 this.el = el || null;
20228                 /**
20229                  * The element id
20230                  * @property id
20231                  */
20232                 this.id = this.el && el.id;
20233                 /**
20234                  * A reference to the style property
20235                  * @property css
20236                  */
20237                 this.css = this.el && el.style;
20238             },
20239
20240         /**
20241          * Returns the X position of an html element
20242          * @method getPosX
20243          * @param el the element for which to get the position
20244          * @return {int} the X coordinate
20245          * @for DragDropMgr
20246          * @deprecated use Roo.lib.Dom.getX instead
20247          * @static
20248          */
20249         getPosX: function(el) {
20250             return Roo.lib.Dom.getX(el);
20251         },
20252
20253         /**
20254          * Returns the Y position of an html element
20255          * @method getPosY
20256          * @param el the element for which to get the position
20257          * @return {int} the Y coordinate
20258          * @deprecated use Roo.lib.Dom.getY instead
20259          * @static
20260          */
20261         getPosY: function(el) {
20262             return Roo.lib.Dom.getY(el);
20263         },
20264
20265         /**
20266          * Swap two nodes.  In IE, we use the native method, for others we
20267          * emulate the IE behavior
20268          * @method swapNode
20269          * @param n1 the first node to swap
20270          * @param n2 the other node to swap
20271          * @static
20272          */
20273         swapNode: function(n1, n2) {
20274             if (n1.swapNode) {
20275                 n1.swapNode(n2);
20276             } else {
20277                 var p = n2.parentNode;
20278                 var s = n2.nextSibling;
20279
20280                 if (s == n1) {
20281                     p.insertBefore(n1, n2);
20282                 } else if (n2 == n1.nextSibling) {
20283                     p.insertBefore(n2, n1);
20284                 } else {
20285                     n1.parentNode.replaceChild(n2, n1);
20286                     p.insertBefore(n1, s);
20287                 }
20288             }
20289         },
20290
20291         /**
20292          * Returns the current scroll position
20293          * @method getScroll
20294          * @private
20295          * @static
20296          */
20297         getScroll: function () {
20298             var t, l, dde=document.documentElement, db=document.body;
20299             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20300                 t = dde.scrollTop;
20301                 l = dde.scrollLeft;
20302             } else if (db) {
20303                 t = db.scrollTop;
20304                 l = db.scrollLeft;
20305             } else {
20306
20307             }
20308             return { top: t, left: l };
20309         },
20310
20311         /**
20312          * Returns the specified element style property
20313          * @method getStyle
20314          * @param {HTMLElement} el          the element
20315          * @param {string}      styleProp   the style property
20316          * @return {string} The value of the style property
20317          * @deprecated use Roo.lib.Dom.getStyle
20318          * @static
20319          */
20320         getStyle: function(el, styleProp) {
20321             return Roo.fly(el).getStyle(styleProp);
20322         },
20323
20324         /**
20325          * Gets the scrollTop
20326          * @method getScrollTop
20327          * @return {int} the document's scrollTop
20328          * @static
20329          */
20330         getScrollTop: function () { return this.getScroll().top; },
20331
20332         /**
20333          * Gets the scrollLeft
20334          * @method getScrollLeft
20335          * @return {int} the document's scrollTop
20336          * @static
20337          */
20338         getScrollLeft: function () { return this.getScroll().left; },
20339
20340         /**
20341          * Sets the x/y position of an element to the location of the
20342          * target element.
20343          * @method moveToEl
20344          * @param {HTMLElement} moveEl      The element to move
20345          * @param {HTMLElement} targetEl    The position reference element
20346          * @static
20347          */
20348         moveToEl: function (moveEl, targetEl) {
20349             var aCoord = Roo.lib.Dom.getXY(targetEl);
20350             Roo.lib.Dom.setXY(moveEl, aCoord);
20351         },
20352
20353         /**
20354          * Numeric array sort function
20355          * @method numericSort
20356          * @static
20357          */
20358         numericSort: function(a, b) { return (a - b); },
20359
20360         /**
20361          * Internal counter
20362          * @property _timeoutCount
20363          * @private
20364          * @static
20365          */
20366         _timeoutCount: 0,
20367
20368         /**
20369          * Trying to make the load order less important.  Without this we get
20370          * an error if this file is loaded before the Event Utility.
20371          * @method _addListeners
20372          * @private
20373          * @static
20374          */
20375         _addListeners: function() {
20376             var DDM = Roo.dd.DDM;
20377             if ( Roo.lib.Event && document ) {
20378                 DDM._onLoad();
20379             } else {
20380                 if (DDM._timeoutCount > 2000) {
20381                 } else {
20382                     setTimeout(DDM._addListeners, 10);
20383                     if (document && document.body) {
20384                         DDM._timeoutCount += 1;
20385                     }
20386                 }
20387             }
20388         },
20389
20390         /**
20391          * Recursively searches the immediate parent and all child nodes for
20392          * the handle element in order to determine wheter or not it was
20393          * clicked.
20394          * @method handleWasClicked
20395          * @param node the html element to inspect
20396          * @static
20397          */
20398         handleWasClicked: function(node, id) {
20399             if (this.isHandle(id, node.id)) {
20400                 return true;
20401             } else {
20402                 // check to see if this is a text node child of the one we want
20403                 var p = node.parentNode;
20404
20405                 while (p) {
20406                     if (this.isHandle(id, p.id)) {
20407                         return true;
20408                     } else {
20409                         p = p.parentNode;
20410                     }
20411                 }
20412             }
20413
20414             return false;
20415         }
20416
20417     };
20418
20419 }();
20420
20421 // shorter alias, save a few bytes
20422 Roo.dd.DDM = Roo.dd.DragDropMgr;
20423 Roo.dd.DDM._addListeners();
20424
20425 }/*
20426  * Based on:
20427  * Ext JS Library 1.1.1
20428  * Copyright(c) 2006-2007, Ext JS, LLC.
20429  *
20430  * Originally Released Under LGPL - original licence link has changed is not relivant.
20431  *
20432  * Fork - LGPL
20433  * <script type="text/javascript">
20434  */
20435
20436 /**
20437  * @class Roo.dd.DD
20438  * A DragDrop implementation where the linked element follows the
20439  * mouse cursor during a drag.
20440  * @extends Roo.dd.DragDrop
20441  * @constructor
20442  * @param {String} id the id of the linked element
20443  * @param {String} sGroup the group of related DragDrop items
20444  * @param {object} config an object containing configurable attributes
20445  *                Valid properties for DD:
20446  *                    scroll
20447  */
20448 Roo.dd.DD = function(id, sGroup, config) {
20449     if (id) {
20450         this.init(id, sGroup, config);
20451     }
20452 };
20453
20454 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20455
20456     /**
20457      * When set to true, the utility automatically tries to scroll the browser
20458      * window wehn a drag and drop element is dragged near the viewport boundary.
20459      * Defaults to true.
20460      * @property scroll
20461      * @type boolean
20462      */
20463     scroll: true,
20464
20465     /**
20466      * Sets the pointer offset to the distance between the linked element's top
20467      * left corner and the location the element was clicked
20468      * @method autoOffset
20469      * @param {int} iPageX the X coordinate of the click
20470      * @param {int} iPageY the Y coordinate of the click
20471      */
20472     autoOffset: function(iPageX, iPageY) {
20473         var x = iPageX - this.startPageX;
20474         var y = iPageY - this.startPageY;
20475         this.setDelta(x, y);
20476     },
20477
20478     /**
20479      * Sets the pointer offset.  You can call this directly to force the
20480      * offset to be in a particular location (e.g., pass in 0,0 to set it
20481      * to the center of the object)
20482      * @method setDelta
20483      * @param {int} iDeltaX the distance from the left
20484      * @param {int} iDeltaY the distance from the top
20485      */
20486     setDelta: function(iDeltaX, iDeltaY) {
20487         this.deltaX = iDeltaX;
20488         this.deltaY = iDeltaY;
20489     },
20490
20491     /**
20492      * Sets the drag element to the location of the mousedown or click event,
20493      * maintaining the cursor location relative to the location on the element
20494      * that was clicked.  Override this if you want to place the element in a
20495      * location other than where the cursor is.
20496      * @method setDragElPos
20497      * @param {int} iPageX the X coordinate of the mousedown or drag event
20498      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20499      */
20500     setDragElPos: function(iPageX, iPageY) {
20501         // the first time we do this, we are going to check to make sure
20502         // the element has css positioning
20503
20504         var el = this.getDragEl();
20505         this.alignElWithMouse(el, iPageX, iPageY);
20506     },
20507
20508     /**
20509      * Sets the element to the location of the mousedown or click event,
20510      * maintaining the cursor location relative to the location on the element
20511      * that was clicked.  Override this if you want to place the element in a
20512      * location other than where the cursor is.
20513      * @method alignElWithMouse
20514      * @param {HTMLElement} el the element to move
20515      * @param {int} iPageX the X coordinate of the mousedown or drag event
20516      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20517      */
20518     alignElWithMouse: function(el, iPageX, iPageY) {
20519         var oCoord = this.getTargetCoord(iPageX, iPageY);
20520         var fly = el.dom ? el : Roo.fly(el);
20521         if (!this.deltaSetXY) {
20522             var aCoord = [oCoord.x, oCoord.y];
20523             fly.setXY(aCoord);
20524             var newLeft = fly.getLeft(true);
20525             var newTop  = fly.getTop(true);
20526             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20527         } else {
20528             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20529         }
20530
20531         this.cachePosition(oCoord.x, oCoord.y);
20532         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20533         return oCoord;
20534     },
20535
20536     /**
20537      * Saves the most recent position so that we can reset the constraints and
20538      * tick marks on-demand.  We need to know this so that we can calculate the
20539      * number of pixels the element is offset from its original position.
20540      * @method cachePosition
20541      * @param iPageX the current x position (optional, this just makes it so we
20542      * don't have to look it up again)
20543      * @param iPageY the current y position (optional, this just makes it so we
20544      * don't have to look it up again)
20545      */
20546     cachePosition: function(iPageX, iPageY) {
20547         if (iPageX) {
20548             this.lastPageX = iPageX;
20549             this.lastPageY = iPageY;
20550         } else {
20551             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20552             this.lastPageX = aCoord[0];
20553             this.lastPageY = aCoord[1];
20554         }
20555     },
20556
20557     /**
20558      * Auto-scroll the window if the dragged object has been moved beyond the
20559      * visible window boundary.
20560      * @method autoScroll
20561      * @param {int} x the drag element's x position
20562      * @param {int} y the drag element's y position
20563      * @param {int} h the height of the drag element
20564      * @param {int} w the width of the drag element
20565      * @private
20566      */
20567     autoScroll: function(x, y, h, w) {
20568
20569         if (this.scroll) {
20570             // The client height
20571             var clientH = Roo.lib.Dom.getViewWidth();
20572
20573             // The client width
20574             var clientW = Roo.lib.Dom.getViewHeight();
20575
20576             // The amt scrolled down
20577             var st = this.DDM.getScrollTop();
20578
20579             // The amt scrolled right
20580             var sl = this.DDM.getScrollLeft();
20581
20582             // Location of the bottom of the element
20583             var bot = h + y;
20584
20585             // Location of the right of the element
20586             var right = w + x;
20587
20588             // The distance from the cursor to the bottom of the visible area,
20589             // adjusted so that we don't scroll if the cursor is beyond the
20590             // element drag constraints
20591             var toBot = (clientH + st - y - this.deltaY);
20592
20593             // The distance from the cursor to the right of the visible area
20594             var toRight = (clientW + sl - x - this.deltaX);
20595
20596
20597             // How close to the edge the cursor must be before we scroll
20598             // var thresh = (document.all) ? 100 : 40;
20599             var thresh = 40;
20600
20601             // How many pixels to scroll per autoscroll op.  This helps to reduce
20602             // clunky scrolling. IE is more sensitive about this ... it needs this
20603             // value to be higher.
20604             var scrAmt = (document.all) ? 80 : 30;
20605
20606             // Scroll down if we are near the bottom of the visible page and the
20607             // obj extends below the crease
20608             if ( bot > clientH && toBot < thresh ) {
20609                 window.scrollTo(sl, st + scrAmt);
20610             }
20611
20612             // Scroll up if the window is scrolled down and the top of the object
20613             // goes above the top border
20614             if ( y < st && st > 0 && y - st < thresh ) {
20615                 window.scrollTo(sl, st - scrAmt);
20616             }
20617
20618             // Scroll right if the obj is beyond the right border and the cursor is
20619             // near the border.
20620             if ( right > clientW && toRight < thresh ) {
20621                 window.scrollTo(sl + scrAmt, st);
20622             }
20623
20624             // Scroll left if the window has been scrolled to the right and the obj
20625             // extends past the left border
20626             if ( x < sl && sl > 0 && x - sl < thresh ) {
20627                 window.scrollTo(sl - scrAmt, st);
20628             }
20629         }
20630     },
20631
20632     /**
20633      * Finds the location the element should be placed if we want to move
20634      * it to where the mouse location less the click offset would place us.
20635      * @method getTargetCoord
20636      * @param {int} iPageX the X coordinate of the click
20637      * @param {int} iPageY the Y coordinate of the click
20638      * @return an object that contains the coordinates (Object.x and Object.y)
20639      * @private
20640      */
20641     getTargetCoord: function(iPageX, iPageY) {
20642
20643
20644         var x = iPageX - this.deltaX;
20645         var y = iPageY - this.deltaY;
20646
20647         if (this.constrainX) {
20648             if (x < this.minX) { x = this.minX; }
20649             if (x > this.maxX) { x = this.maxX; }
20650         }
20651
20652         if (this.constrainY) {
20653             if (y < this.minY) { y = this.minY; }
20654             if (y > this.maxY) { y = this.maxY; }
20655         }
20656
20657         x = this.getTick(x, this.xTicks);
20658         y = this.getTick(y, this.yTicks);
20659
20660
20661         return {x:x, y:y};
20662     },
20663
20664     /*
20665      * Sets up config options specific to this class. Overrides
20666      * Roo.dd.DragDrop, but all versions of this method through the
20667      * inheritance chain are called
20668      */
20669     applyConfig: function() {
20670         Roo.dd.DD.superclass.applyConfig.call(this);
20671         this.scroll = (this.config.scroll !== false);
20672     },
20673
20674     /*
20675      * Event that fires prior to the onMouseDown event.  Overrides
20676      * Roo.dd.DragDrop.
20677      */
20678     b4MouseDown: function(e) {
20679         // this.resetConstraints();
20680         this.autoOffset(e.getPageX(),
20681                             e.getPageY());
20682     },
20683
20684     /*
20685      * Event that fires prior to the onDrag event.  Overrides
20686      * Roo.dd.DragDrop.
20687      */
20688     b4Drag: function(e) {
20689         this.setDragElPos(e.getPageX(),
20690                             e.getPageY());
20691     },
20692
20693     toString: function() {
20694         return ("DD " + this.id);
20695     }
20696
20697     //////////////////////////////////////////////////////////////////////////
20698     // Debugging ygDragDrop events that can be overridden
20699     //////////////////////////////////////////////////////////////////////////
20700     /*
20701     startDrag: function(x, y) {
20702     },
20703
20704     onDrag: function(e) {
20705     },
20706
20707     onDragEnter: function(e, id) {
20708     },
20709
20710     onDragOver: function(e, id) {
20711     },
20712
20713     onDragOut: function(e, id) {
20714     },
20715
20716     onDragDrop: function(e, id) {
20717     },
20718
20719     endDrag: function(e) {
20720     }
20721
20722     */
20723
20724 });/*
20725  * Based on:
20726  * Ext JS Library 1.1.1
20727  * Copyright(c) 2006-2007, Ext JS, LLC.
20728  *
20729  * Originally Released Under LGPL - original licence link has changed is not relivant.
20730  *
20731  * Fork - LGPL
20732  * <script type="text/javascript">
20733  */
20734
20735 /**
20736  * @class Roo.dd.DDProxy
20737  * A DragDrop implementation that inserts an empty, bordered div into
20738  * the document that follows the cursor during drag operations.  At the time of
20739  * the click, the frame div is resized to the dimensions of the linked html
20740  * element, and moved to the exact location of the linked element.
20741  *
20742  * References to the "frame" element refer to the single proxy element that
20743  * was created to be dragged in place of all DDProxy elements on the
20744  * page.
20745  *
20746  * @extends Roo.dd.DD
20747  * @constructor
20748  * @param {String} id the id of the linked html element
20749  * @param {String} sGroup the group of related DragDrop objects
20750  * @param {object} config an object containing configurable attributes
20751  *                Valid properties for DDProxy in addition to those in DragDrop:
20752  *                   resizeFrame, centerFrame, dragElId
20753  */
20754 Roo.dd.DDProxy = function(id, sGroup, config) {
20755     if (id) {
20756         this.init(id, sGroup, config);
20757         this.initFrame();
20758     }
20759 };
20760
20761 /**
20762  * The default drag frame div id
20763  * @property Roo.dd.DDProxy.dragElId
20764  * @type String
20765  * @static
20766  */
20767 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20768
20769 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20770
20771     /**
20772      * By default we resize the drag frame to be the same size as the element
20773      * we want to drag (this is to get the frame effect).  We can turn it off
20774      * if we want a different behavior.
20775      * @property resizeFrame
20776      * @type boolean
20777      */
20778     resizeFrame: true,
20779
20780     /**
20781      * By default the frame is positioned exactly where the drag element is, so
20782      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20783      * you do not have constraints on the obj is to have the drag frame centered
20784      * around the cursor.  Set centerFrame to true for this effect.
20785      * @property centerFrame
20786      * @type boolean
20787      */
20788     centerFrame: false,
20789
20790     /**
20791      * Creates the proxy element if it does not yet exist
20792      * @method createFrame
20793      */
20794     createFrame: function() {
20795         var self = this;
20796         var body = document.body;
20797
20798         if (!body || !body.firstChild) {
20799             setTimeout( function() { self.createFrame(); }, 50 );
20800             return;
20801         }
20802
20803         var div = this.getDragEl();
20804
20805         if (!div) {
20806             div    = document.createElement("div");
20807             div.id = this.dragElId;
20808             var s  = div.style;
20809
20810             s.position   = "absolute";
20811             s.visibility = "hidden";
20812             s.cursor     = "move";
20813             s.border     = "2px solid #aaa";
20814             s.zIndex     = 999;
20815
20816             // appendChild can blow up IE if invoked prior to the window load event
20817             // while rendering a table.  It is possible there are other scenarios
20818             // that would cause this to happen as well.
20819             body.insertBefore(div, body.firstChild);
20820         }
20821     },
20822
20823     /**
20824      * Initialization for the drag frame element.  Must be called in the
20825      * constructor of all subclasses
20826      * @method initFrame
20827      */
20828     initFrame: function() {
20829         this.createFrame();
20830     },
20831
20832     applyConfig: function() {
20833         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20834
20835         this.resizeFrame = (this.config.resizeFrame !== false);
20836         this.centerFrame = (this.config.centerFrame);
20837         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20838     },
20839
20840     /**
20841      * Resizes the drag frame to the dimensions of the clicked object, positions
20842      * it over the object, and finally displays it
20843      * @method showFrame
20844      * @param {int} iPageX X click position
20845      * @param {int} iPageY Y click position
20846      * @private
20847      */
20848     showFrame: function(iPageX, iPageY) {
20849         var el = this.getEl();
20850         var dragEl = this.getDragEl();
20851         var s = dragEl.style;
20852
20853         this._resizeProxy();
20854
20855         if (this.centerFrame) {
20856             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20857                            Math.round(parseInt(s.height, 10)/2) );
20858         }
20859
20860         this.setDragElPos(iPageX, iPageY);
20861
20862         Roo.fly(dragEl).show();
20863     },
20864
20865     /**
20866      * The proxy is automatically resized to the dimensions of the linked
20867      * element when a drag is initiated, unless resizeFrame is set to false
20868      * @method _resizeProxy
20869      * @private
20870      */
20871     _resizeProxy: function() {
20872         if (this.resizeFrame) {
20873             var el = this.getEl();
20874             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20875         }
20876     },
20877
20878     // overrides Roo.dd.DragDrop
20879     b4MouseDown: function(e) {
20880         var x = e.getPageX();
20881         var y = e.getPageY();
20882         this.autoOffset(x, y);
20883         this.setDragElPos(x, y);
20884     },
20885
20886     // overrides Roo.dd.DragDrop
20887     b4StartDrag: function(x, y) {
20888         // show the drag frame
20889         this.showFrame(x, y);
20890     },
20891
20892     // overrides Roo.dd.DragDrop
20893     b4EndDrag: function(e) {
20894         Roo.fly(this.getDragEl()).hide();
20895     },
20896
20897     // overrides Roo.dd.DragDrop
20898     // By default we try to move the element to the last location of the frame.
20899     // This is so that the default behavior mirrors that of Roo.dd.DD.
20900     endDrag: function(e) {
20901
20902         var lel = this.getEl();
20903         var del = this.getDragEl();
20904
20905         // Show the drag frame briefly so we can get its position
20906         del.style.visibility = "";
20907
20908         this.beforeMove();
20909         // Hide the linked element before the move to get around a Safari
20910         // rendering bug.
20911         lel.style.visibility = "hidden";
20912         Roo.dd.DDM.moveToEl(lel, del);
20913         del.style.visibility = "hidden";
20914         lel.style.visibility = "";
20915
20916         this.afterDrag();
20917     },
20918
20919     beforeMove : function(){
20920
20921     },
20922
20923     afterDrag : function(){
20924
20925     },
20926
20927     toString: function() {
20928         return ("DDProxy " + this.id);
20929     }
20930
20931 });
20932 /*
20933  * Based on:
20934  * Ext JS Library 1.1.1
20935  * Copyright(c) 2006-2007, Ext JS, LLC.
20936  *
20937  * Originally Released Under LGPL - original licence link has changed is not relivant.
20938  *
20939  * Fork - LGPL
20940  * <script type="text/javascript">
20941  */
20942
20943  /**
20944  * @class Roo.dd.DDTarget
20945  * A DragDrop implementation that does not move, but can be a drop
20946  * target.  You would get the same result by simply omitting implementation
20947  * for the event callbacks, but this way we reduce the processing cost of the
20948  * event listener and the callbacks.
20949  * @extends Roo.dd.DragDrop
20950  * @constructor
20951  * @param {String} id the id of the element that is a drop target
20952  * @param {String} sGroup the group of related DragDrop objects
20953  * @param {object} config an object containing configurable attributes
20954  *                 Valid properties for DDTarget in addition to those in
20955  *                 DragDrop:
20956  *                    none
20957  */
20958 Roo.dd.DDTarget = function(id, sGroup, config) {
20959     if (id) {
20960         this.initTarget(id, sGroup, config);
20961     }
20962     if (config.listeners || config.events) { 
20963        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20964             listeners : config.listeners || {}, 
20965             events : config.events || {} 
20966         });    
20967     }
20968 };
20969
20970 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20971 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20972     toString: function() {
20973         return ("DDTarget " + this.id);
20974     }
20975 });
20976 /*
20977  * Based on:
20978  * Ext JS Library 1.1.1
20979  * Copyright(c) 2006-2007, Ext JS, LLC.
20980  *
20981  * Originally Released Under LGPL - original licence link has changed is not relivant.
20982  *
20983  * Fork - LGPL
20984  * <script type="text/javascript">
20985  */
20986  
20987
20988 /**
20989  * @class Roo.dd.ScrollManager
20990  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
20991  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
20992  * @singleton
20993  */
20994 Roo.dd.ScrollManager = function(){
20995     var ddm = Roo.dd.DragDropMgr;
20996     var els = {};
20997     var dragEl = null;
20998     var proc = {};
20999     
21000     
21001     
21002     var onStop = function(e){
21003         dragEl = null;
21004         clearProc();
21005     };
21006     
21007     var triggerRefresh = function(){
21008         if(ddm.dragCurrent){
21009              ddm.refreshCache(ddm.dragCurrent.groups);
21010         }
21011     };
21012     
21013     var doScroll = function(){
21014         if(ddm.dragCurrent){
21015             var dds = Roo.dd.ScrollManager;
21016             if(!dds.animate){
21017                 if(proc.el.scroll(proc.dir, dds.increment)){
21018                     triggerRefresh();
21019                 }
21020             }else{
21021                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21022             }
21023         }
21024     };
21025     
21026     var clearProc = function(){
21027         if(proc.id){
21028             clearInterval(proc.id);
21029         }
21030         proc.id = 0;
21031         proc.el = null;
21032         proc.dir = "";
21033     };
21034     
21035     var startProc = function(el, dir){
21036          Roo.log('scroll startproc');
21037         clearProc();
21038         proc.el = el;
21039         proc.dir = dir;
21040         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21041     };
21042     
21043     var onFire = function(e, isDrop){
21044        
21045         if(isDrop || !ddm.dragCurrent){ return; }
21046         var dds = Roo.dd.ScrollManager;
21047         if(!dragEl || dragEl != ddm.dragCurrent){
21048             dragEl = ddm.dragCurrent;
21049             // refresh regions on drag start
21050             dds.refreshCache();
21051         }
21052         
21053         var xy = Roo.lib.Event.getXY(e);
21054         var pt = new Roo.lib.Point(xy[0], xy[1]);
21055         for(var id in els){
21056             var el = els[id], r = el._region;
21057             if(r && r.contains(pt) && el.isScrollable()){
21058                 if(r.bottom - pt.y <= dds.thresh){
21059                     if(proc.el != el){
21060                         startProc(el, "down");
21061                     }
21062                     return;
21063                 }else if(r.right - pt.x <= dds.thresh){
21064                     if(proc.el != el){
21065                         startProc(el, "left");
21066                     }
21067                     return;
21068                 }else if(pt.y - r.top <= dds.thresh){
21069                     if(proc.el != el){
21070                         startProc(el, "up");
21071                     }
21072                     return;
21073                 }else if(pt.x - r.left <= dds.thresh){
21074                     if(proc.el != el){
21075                         startProc(el, "right");
21076                     }
21077                     return;
21078                 }
21079             }
21080         }
21081         clearProc();
21082     };
21083     
21084     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21085     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21086     
21087     return {
21088         /**
21089          * Registers new overflow element(s) to auto scroll
21090          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21091          */
21092         register : function(el){
21093             if(el instanceof Array){
21094                 for(var i = 0, len = el.length; i < len; i++) {
21095                         this.register(el[i]);
21096                 }
21097             }else{
21098                 el = Roo.get(el);
21099                 els[el.id] = el;
21100             }
21101             Roo.dd.ScrollManager.els = els;
21102         },
21103         
21104         /**
21105          * Unregisters overflow element(s) so they are no longer scrolled
21106          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21107          */
21108         unregister : function(el){
21109             if(el instanceof Array){
21110                 for(var i = 0, len = el.length; i < len; i++) {
21111                         this.unregister(el[i]);
21112                 }
21113             }else{
21114                 el = Roo.get(el);
21115                 delete els[el.id];
21116             }
21117         },
21118         
21119         /**
21120          * The number of pixels from the edge of a container the pointer needs to be to 
21121          * trigger scrolling (defaults to 25)
21122          * @type Number
21123          */
21124         thresh : 25,
21125         
21126         /**
21127          * The number of pixels to scroll in each scroll increment (defaults to 50)
21128          * @type Number
21129          */
21130         increment : 100,
21131         
21132         /**
21133          * The frequency of scrolls in milliseconds (defaults to 500)
21134          * @type Number
21135          */
21136         frequency : 500,
21137         
21138         /**
21139          * True to animate the scroll (defaults to true)
21140          * @type Boolean
21141          */
21142         animate: true,
21143         
21144         /**
21145          * The animation duration in seconds - 
21146          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21147          * @type Number
21148          */
21149         animDuration: .4,
21150         
21151         /**
21152          * Manually trigger a cache refresh.
21153          */
21154         refreshCache : function(){
21155             for(var id in els){
21156                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21157                     els[id]._region = els[id].getRegion();
21158                 }
21159             }
21160         }
21161     };
21162 }();/*
21163  * Based on:
21164  * Ext JS Library 1.1.1
21165  * Copyright(c) 2006-2007, Ext JS, LLC.
21166  *
21167  * Originally Released Under LGPL - original licence link has changed is not relivant.
21168  *
21169  * Fork - LGPL
21170  * <script type="text/javascript">
21171  */
21172  
21173
21174 /**
21175  * @class Roo.dd.Registry
21176  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21177  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21178  * @singleton
21179  */
21180 Roo.dd.Registry = function(){
21181     var elements = {}; 
21182     var handles = {}; 
21183     var autoIdSeed = 0;
21184
21185     var getId = function(el, autogen){
21186         if(typeof el == "string"){
21187             return el;
21188         }
21189         var id = el.id;
21190         if(!id && autogen !== false){
21191             id = "roodd-" + (++autoIdSeed);
21192             el.id = id;
21193         }
21194         return id;
21195     };
21196     
21197     return {
21198     /**
21199      * Register a drag drop element
21200      * @param {String|HTMLElement} element The id or DOM node to register
21201      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21202      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21203      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21204      * populated in the data object (if applicable):
21205      * <pre>
21206 Value      Description<br />
21207 ---------  ------------------------------------------<br />
21208 handles    Array of DOM nodes that trigger dragging<br />
21209            for the element being registered<br />
21210 isHandle   True if the element passed in triggers<br />
21211            dragging itself, else false
21212 </pre>
21213      */
21214         register : function(el, data){
21215             data = data || {};
21216             if(typeof el == "string"){
21217                 el = document.getElementById(el);
21218             }
21219             data.ddel = el;
21220             elements[getId(el)] = data;
21221             if(data.isHandle !== false){
21222                 handles[data.ddel.id] = data;
21223             }
21224             if(data.handles){
21225                 var hs = data.handles;
21226                 for(var i = 0, len = hs.length; i < len; i++){
21227                         handles[getId(hs[i])] = data;
21228                 }
21229             }
21230         },
21231
21232     /**
21233      * Unregister a drag drop element
21234      * @param {String|HTMLElement}  element The id or DOM node to unregister
21235      */
21236         unregister : function(el){
21237             var id = getId(el, false);
21238             var data = elements[id];
21239             if(data){
21240                 delete elements[id];
21241                 if(data.handles){
21242                     var hs = data.handles;
21243                     for(var i = 0, len = hs.length; i < len; i++){
21244                         delete handles[getId(hs[i], false)];
21245                     }
21246                 }
21247             }
21248         },
21249
21250     /**
21251      * Returns the handle registered for a DOM Node by id
21252      * @param {String|HTMLElement} id The DOM node or id to look up
21253      * @return {Object} handle The custom handle data
21254      */
21255         getHandle : function(id){
21256             if(typeof id != "string"){ // must be element?
21257                 id = id.id;
21258             }
21259             return handles[id];
21260         },
21261
21262     /**
21263      * Returns the handle that is registered for the DOM node that is the target of the event
21264      * @param {Event} e The event
21265      * @return {Object} handle The custom handle data
21266      */
21267         getHandleFromEvent : function(e){
21268             var t = Roo.lib.Event.getTarget(e);
21269             return t ? handles[t.id] : null;
21270         },
21271
21272     /**
21273      * Returns a custom data object that is registered for a DOM node by id
21274      * @param {String|HTMLElement} id The DOM node or id to look up
21275      * @return {Object} data The custom data
21276      */
21277         getTarget : function(id){
21278             if(typeof id != "string"){ // must be element?
21279                 id = id.id;
21280             }
21281             return elements[id];
21282         },
21283
21284     /**
21285      * Returns a custom data object that is registered for the DOM node that is the target of the event
21286      * @param {Event} e The event
21287      * @return {Object} data The custom data
21288      */
21289         getTargetFromEvent : function(e){
21290             var t = Roo.lib.Event.getTarget(e);
21291             return t ? elements[t.id] || handles[t.id] : null;
21292         }
21293     };
21294 }();/*
21295  * Based on:
21296  * Ext JS Library 1.1.1
21297  * Copyright(c) 2006-2007, Ext JS, LLC.
21298  *
21299  * Originally Released Under LGPL - original licence link has changed is not relivant.
21300  *
21301  * Fork - LGPL
21302  * <script type="text/javascript">
21303  */
21304  
21305
21306 /**
21307  * @class Roo.dd.StatusProxy
21308  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21309  * default drag proxy used by all Roo.dd components.
21310  * @constructor
21311  * @param {Object} config
21312  */
21313 Roo.dd.StatusProxy = function(config){
21314     Roo.apply(this, config);
21315     this.id = this.id || Roo.id();
21316     this.el = new Roo.Layer({
21317         dh: {
21318             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21319                 {tag: "div", cls: "x-dd-drop-icon"},
21320                 {tag: "div", cls: "x-dd-drag-ghost"}
21321             ]
21322         }, 
21323         shadow: !config || config.shadow !== false
21324     });
21325     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21326     this.dropStatus = this.dropNotAllowed;
21327 };
21328
21329 Roo.dd.StatusProxy.prototype = {
21330     /**
21331      * @cfg {String} dropAllowed
21332      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21333      */
21334     dropAllowed : "x-dd-drop-ok",
21335     /**
21336      * @cfg {String} dropNotAllowed
21337      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21338      */
21339     dropNotAllowed : "x-dd-drop-nodrop",
21340
21341     /**
21342      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21343      * over the current target element.
21344      * @param {String} cssClass The css class for the new drop status indicator image
21345      */
21346     setStatus : function(cssClass){
21347         cssClass = cssClass || this.dropNotAllowed;
21348         if(this.dropStatus != cssClass){
21349             this.el.replaceClass(this.dropStatus, cssClass);
21350             this.dropStatus = cssClass;
21351         }
21352     },
21353
21354     /**
21355      * Resets the status indicator to the default dropNotAllowed value
21356      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21357      */
21358     reset : function(clearGhost){
21359         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21360         this.dropStatus = this.dropNotAllowed;
21361         if(clearGhost){
21362             this.ghost.update("");
21363         }
21364     },
21365
21366     /**
21367      * Updates the contents of the ghost element
21368      * @param {String} html The html that will replace the current innerHTML of the ghost element
21369      */
21370     update : function(html){
21371         if(typeof html == "string"){
21372             this.ghost.update(html);
21373         }else{
21374             this.ghost.update("");
21375             html.style.margin = "0";
21376             this.ghost.dom.appendChild(html);
21377         }
21378         // ensure float = none set?? cant remember why though.
21379         var el = this.ghost.dom.firstChild;
21380                 if(el){
21381                         Roo.fly(el).setStyle('float', 'none');
21382                 }
21383     },
21384     
21385     /**
21386      * Returns the underlying proxy {@link Roo.Layer}
21387      * @return {Roo.Layer} el
21388     */
21389     getEl : function(){
21390         return this.el;
21391     },
21392
21393     /**
21394      * Returns the ghost element
21395      * @return {Roo.Element} el
21396      */
21397     getGhost : function(){
21398         return this.ghost;
21399     },
21400
21401     /**
21402      * Hides the proxy
21403      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21404      */
21405     hide : function(clear){
21406         this.el.hide();
21407         if(clear){
21408             this.reset(true);
21409         }
21410     },
21411
21412     /**
21413      * Stops the repair animation if it's currently running
21414      */
21415     stop : function(){
21416         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21417             this.anim.stop();
21418         }
21419     },
21420
21421     /**
21422      * Displays this proxy
21423      */
21424     show : function(){
21425         this.el.show();
21426     },
21427
21428     /**
21429      * Force the Layer to sync its shadow and shim positions to the element
21430      */
21431     sync : function(){
21432         this.el.sync();
21433     },
21434
21435     /**
21436      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21437      * invalid drop operation by the item being dragged.
21438      * @param {Array} xy The XY position of the element ([x, y])
21439      * @param {Function} callback The function to call after the repair is complete
21440      * @param {Object} scope The scope in which to execute the callback
21441      */
21442     repair : function(xy, callback, scope){
21443         this.callback = callback;
21444         this.scope = scope;
21445         if(xy && this.animRepair !== false){
21446             this.el.addClass("x-dd-drag-repair");
21447             this.el.hideUnders(true);
21448             this.anim = this.el.shift({
21449                 duration: this.repairDuration || .5,
21450                 easing: 'easeOut',
21451                 xy: xy,
21452                 stopFx: true,
21453                 callback: this.afterRepair,
21454                 scope: this
21455             });
21456         }else{
21457             this.afterRepair();
21458         }
21459     },
21460
21461     // private
21462     afterRepair : function(){
21463         this.hide(true);
21464         if(typeof this.callback == "function"){
21465             this.callback.call(this.scope || this);
21466         }
21467         this.callback = null;
21468         this.scope = null;
21469     }
21470 };/*
21471  * Based on:
21472  * Ext JS Library 1.1.1
21473  * Copyright(c) 2006-2007, Ext JS, LLC.
21474  *
21475  * Originally Released Under LGPL - original licence link has changed is not relivant.
21476  *
21477  * Fork - LGPL
21478  * <script type="text/javascript">
21479  */
21480
21481 /**
21482  * @class Roo.dd.DragSource
21483  * @extends Roo.dd.DDProxy
21484  * A simple class that provides the basic implementation needed to make any element draggable.
21485  * @constructor
21486  * @param {String/HTMLElement/Element} el The container element
21487  * @param {Object} config
21488  */
21489 Roo.dd.DragSource = function(el, config){
21490     this.el = Roo.get(el);
21491     this.dragData = {};
21492     
21493     Roo.apply(this, config);
21494     
21495     if(!this.proxy){
21496         this.proxy = new Roo.dd.StatusProxy();
21497     }
21498
21499     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21500           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21501     
21502     this.dragging = false;
21503 };
21504
21505 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21506     /**
21507      * @cfg {String} dropAllowed
21508      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21509      */
21510     dropAllowed : "x-dd-drop-ok",
21511     /**
21512      * @cfg {String} dropNotAllowed
21513      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21514      */
21515     dropNotAllowed : "x-dd-drop-nodrop",
21516
21517     /**
21518      * Returns the data object associated with this drag source
21519      * @return {Object} data An object containing arbitrary data
21520      */
21521     getDragData : function(e){
21522         return this.dragData;
21523     },
21524
21525     // private
21526     onDragEnter : function(e, id){
21527         var target = Roo.dd.DragDropMgr.getDDById(id);
21528         this.cachedTarget = target;
21529         if(this.beforeDragEnter(target, e, id) !== false){
21530             if(target.isNotifyTarget){
21531                 var status = target.notifyEnter(this, e, this.dragData);
21532                 this.proxy.setStatus(status);
21533             }else{
21534                 this.proxy.setStatus(this.dropAllowed);
21535             }
21536             
21537             if(this.afterDragEnter){
21538                 /**
21539                  * An empty function by default, but provided so that you can perform a custom action
21540                  * when the dragged item enters the drop target by providing an implementation.
21541                  * @param {Roo.dd.DragDrop} target The drop target
21542                  * @param {Event} e The event object
21543                  * @param {String} id The id of the dragged element
21544                  * @method afterDragEnter
21545                  */
21546                 this.afterDragEnter(target, e, id);
21547             }
21548         }
21549     },
21550
21551     /**
21552      * An empty function by default, but provided so that you can perform a custom action
21553      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21554      * @param {Roo.dd.DragDrop} target The drop target
21555      * @param {Event} e The event object
21556      * @param {String} id The id of the dragged element
21557      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21558      */
21559     beforeDragEnter : function(target, e, id){
21560         return true;
21561     },
21562
21563     // private
21564     alignElWithMouse: function() {
21565         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21566         this.proxy.sync();
21567     },
21568
21569     // private
21570     onDragOver : function(e, id){
21571         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21572         if(this.beforeDragOver(target, e, id) !== false){
21573             if(target.isNotifyTarget){
21574                 var status = target.notifyOver(this, e, this.dragData);
21575                 this.proxy.setStatus(status);
21576             }
21577
21578             if(this.afterDragOver){
21579                 /**
21580                  * An empty function by default, but provided so that you can perform a custom action
21581                  * while the dragged item is over the drop target by providing an implementation.
21582                  * @param {Roo.dd.DragDrop} target The drop target
21583                  * @param {Event} e The event object
21584                  * @param {String} id The id of the dragged element
21585                  * @method afterDragOver
21586                  */
21587                 this.afterDragOver(target, e, id);
21588             }
21589         }
21590     },
21591
21592     /**
21593      * An empty function by default, but provided so that you can perform a custom action
21594      * while the dragged item is over the drop target and optionally cancel the onDragOver.
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      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21599      */
21600     beforeDragOver : function(target, e, id){
21601         return true;
21602     },
21603
21604     // private
21605     onDragOut : function(e, id){
21606         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21607         if(this.beforeDragOut(target, e, id) !== false){
21608             if(target.isNotifyTarget){
21609                 target.notifyOut(this, e, this.dragData);
21610             }
21611             this.proxy.reset();
21612             if(this.afterDragOut){
21613                 /**
21614                  * An empty function by default, but provided so that you can perform a custom action
21615                  * after the dragged item is dragged out of the target without dropping.
21616                  * @param {Roo.dd.DragDrop} target The drop target
21617                  * @param {Event} e The event object
21618                  * @param {String} id The id of the dragged element
21619                  * @method afterDragOut
21620                  */
21621                 this.afterDragOut(target, e, id);
21622             }
21623         }
21624         this.cachedTarget = null;
21625     },
21626
21627     /**
21628      * An empty function by default, but provided so that you can perform a custom action before the dragged
21629      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21630      * @param {Roo.dd.DragDrop} target The drop target
21631      * @param {Event} e The event object
21632      * @param {String} id The id of the dragged element
21633      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21634      */
21635     beforeDragOut : function(target, e, id){
21636         return true;
21637     },
21638     
21639     // private
21640     onDragDrop : function(e, id){
21641         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21642         if(this.beforeDragDrop(target, e, id) !== false){
21643             if(target.isNotifyTarget){
21644                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21645                     this.onValidDrop(target, e, id);
21646                 }else{
21647                     this.onInvalidDrop(target, e, id);
21648                 }
21649             }else{
21650                 this.onValidDrop(target, e, id);
21651             }
21652             
21653             if(this.afterDragDrop){
21654                 /**
21655                  * An empty function by default, but provided so that you can perform a custom action
21656                  * after a valid drag drop has occurred by providing an implementation.
21657                  * @param {Roo.dd.DragDrop} target The drop target
21658                  * @param {Event} e The event object
21659                  * @param {String} id The id of the dropped element
21660                  * @method afterDragDrop
21661                  */
21662                 this.afterDragDrop(target, e, id);
21663             }
21664         }
21665         delete this.cachedTarget;
21666     },
21667
21668     /**
21669      * An empty function by default, but provided so that you can perform a custom action before the dragged
21670      * item is dropped onto the target and optionally cancel the onDragDrop.
21671      * @param {Roo.dd.DragDrop} target The drop target
21672      * @param {Event} e The event object
21673      * @param {String} id The id of the dragged element
21674      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21675      */
21676     beforeDragDrop : function(target, e, id){
21677         return true;
21678     },
21679
21680     // private
21681     onValidDrop : function(target, e, id){
21682         this.hideProxy();
21683         if(this.afterValidDrop){
21684             /**
21685              * An empty function by default, but provided so that you can perform a custom action
21686              * after a valid drop has occurred by providing an implementation.
21687              * @param {Object} target The target DD 
21688              * @param {Event} e The event object
21689              * @param {String} id The id of the dropped element
21690              * @method afterInvalidDrop
21691              */
21692             this.afterValidDrop(target, e, id);
21693         }
21694     },
21695
21696     // private
21697     getRepairXY : function(e, data){
21698         return this.el.getXY();  
21699     },
21700
21701     // private
21702     onInvalidDrop : function(target, e, id){
21703         this.beforeInvalidDrop(target, e, id);
21704         if(this.cachedTarget){
21705             if(this.cachedTarget.isNotifyTarget){
21706                 this.cachedTarget.notifyOut(this, e, this.dragData);
21707             }
21708             this.cacheTarget = null;
21709         }
21710         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21711
21712         if(this.afterInvalidDrop){
21713             /**
21714              * An empty function by default, but provided so that you can perform a custom action
21715              * after an invalid drop has occurred by providing an implementation.
21716              * @param {Event} e The event object
21717              * @param {String} id The id of the dropped element
21718              * @method afterInvalidDrop
21719              */
21720             this.afterInvalidDrop(e, id);
21721         }
21722     },
21723
21724     // private
21725     afterRepair : function(){
21726         if(Roo.enableFx){
21727             this.el.highlight(this.hlColor || "c3daf9");
21728         }
21729         this.dragging = false;
21730     },
21731
21732     /**
21733      * An empty function by default, but provided so that you can perform a custom action after an invalid
21734      * drop has occurred.
21735      * @param {Roo.dd.DragDrop} target The drop target
21736      * @param {Event} e The event object
21737      * @param {String} id The id of the dragged element
21738      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21739      */
21740     beforeInvalidDrop : function(target, e, id){
21741         return true;
21742     },
21743
21744     // private
21745     handleMouseDown : function(e){
21746         if(this.dragging) {
21747             return;
21748         }
21749         var data = this.getDragData(e);
21750         if(data && this.onBeforeDrag(data, e) !== false){
21751             this.dragData = data;
21752             this.proxy.stop();
21753             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21754         } 
21755     },
21756
21757     /**
21758      * An empty function by default, but provided so that you can perform a custom action before the initial
21759      * drag event begins and optionally cancel it.
21760      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21761      * @param {Event} e The event object
21762      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21763      */
21764     onBeforeDrag : function(data, e){
21765         return true;
21766     },
21767
21768     /**
21769      * An empty function by default, but provided so that you can perform a custom action once the initial
21770      * drag event has begun.  The drag cannot be canceled from this function.
21771      * @param {Number} x The x position of the click on the dragged object
21772      * @param {Number} y The y position of the click on the dragged object
21773      */
21774     onStartDrag : Roo.emptyFn,
21775
21776     // private - YUI override
21777     startDrag : function(x, y){
21778         this.proxy.reset();
21779         this.dragging = true;
21780         this.proxy.update("");
21781         this.onInitDrag(x, y);
21782         this.proxy.show();
21783     },
21784
21785     // private
21786     onInitDrag : function(x, y){
21787         var clone = this.el.dom.cloneNode(true);
21788         clone.id = Roo.id(); // prevent duplicate ids
21789         this.proxy.update(clone);
21790         this.onStartDrag(x, y);
21791         return true;
21792     },
21793
21794     /**
21795      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21796      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21797      */
21798     getProxy : function(){
21799         return this.proxy;  
21800     },
21801
21802     /**
21803      * Hides the drag source's {@link Roo.dd.StatusProxy}
21804      */
21805     hideProxy : function(){
21806         this.proxy.hide();  
21807         this.proxy.reset(true);
21808         this.dragging = false;
21809     },
21810
21811     // private
21812     triggerCacheRefresh : function(){
21813         Roo.dd.DDM.refreshCache(this.groups);
21814     },
21815
21816     // private - override to prevent hiding
21817     b4EndDrag: function(e) {
21818     },
21819
21820     // private - override to prevent moving
21821     endDrag : function(e){
21822         this.onEndDrag(this.dragData, e);
21823     },
21824
21825     // private
21826     onEndDrag : function(data, e){
21827     },
21828     
21829     // private - pin to cursor
21830     autoOffset : function(x, y) {
21831         this.setDelta(-12, -20);
21832     }    
21833 });/*
21834  * Based on:
21835  * Ext JS Library 1.1.1
21836  * Copyright(c) 2006-2007, Ext JS, LLC.
21837  *
21838  * Originally Released Under LGPL - original licence link has changed is not relivant.
21839  *
21840  * Fork - LGPL
21841  * <script type="text/javascript">
21842  */
21843
21844
21845 /**
21846  * @class Roo.dd.DropTarget
21847  * @extends Roo.dd.DDTarget
21848  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21849  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21850  * @constructor
21851  * @param {String/HTMLElement/Element} el The container element
21852  * @param {Object} config
21853  */
21854 Roo.dd.DropTarget = function(el, config){
21855     this.el = Roo.get(el);
21856     
21857     var listeners = false; ;
21858     if (config && config.listeners) {
21859         listeners= config.listeners;
21860         delete config.listeners;
21861     }
21862     Roo.apply(this, config);
21863     
21864     if(this.containerScroll){
21865         Roo.dd.ScrollManager.register(this.el);
21866     }
21867     this.addEvents( {
21868          /**
21869          * @scope Roo.dd.DropTarget
21870          */
21871          
21872          /**
21873          * @event enter
21874          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21875          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21876          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21877          * 
21878          * IMPORTANT : it should set this.overClass and this.dropAllowed
21879          * 
21880          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21881          * @param {Event} e The event
21882          * @param {Object} data An object containing arbitrary data supplied by the drag source
21883          */
21884         "enter" : true,
21885         
21886          /**
21887          * @event over
21888          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21889          * This method will be called on every mouse movement while the drag source is over the drop target.
21890          * This default implementation simply returns the dropAllowed config value.
21891          * 
21892          * IMPORTANT : it should set this.dropAllowed
21893          * 
21894          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21895          * @param {Event} e The event
21896          * @param {Object} data An object containing arbitrary data supplied by the drag source
21897          
21898          */
21899         "over" : true,
21900         /**
21901          * @event out
21902          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21903          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21904          * overClass (if any) from the drop element.
21905          * 
21906          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21907          * @param {Event} e The event
21908          * @param {Object} data An object containing arbitrary data supplied by the drag source
21909          */
21910          "out" : true,
21911          
21912         /**
21913          * @event drop
21914          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21915          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21916          * implementation that does something to process the drop event and returns true so that the drag source's
21917          * repair action does not run.
21918          * 
21919          * IMPORTANT : it should set this.success
21920          * 
21921          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21922          * @param {Event} e The event
21923          * @param {Object} data An object containing arbitrary data supplied by the drag source
21924         */
21925          "drop" : true
21926     });
21927             
21928      
21929     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21930         this.el.dom, 
21931         this.ddGroup || this.group,
21932         {
21933             isTarget: true,
21934             listeners : listeners || {} 
21935            
21936         
21937         }
21938     );
21939
21940 };
21941
21942 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21943     /**
21944      * @cfg {String} overClass
21945      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21946      */
21947      /**
21948      * @cfg {String} ddGroup
21949      * The drag drop group to handle drop events for
21950      */
21951      
21952     /**
21953      * @cfg {String} dropAllowed
21954      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21955      */
21956     dropAllowed : "x-dd-drop-ok",
21957     /**
21958      * @cfg {String} dropNotAllowed
21959      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21960      */
21961     dropNotAllowed : "x-dd-drop-nodrop",
21962     /**
21963      * @cfg {boolean} success
21964      * set this after drop listener.. 
21965      */
21966     success : false,
21967     /**
21968      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21969      * if the drop point is valid for over/enter..
21970      */
21971     valid : false,
21972     // private
21973     isTarget : true,
21974
21975     // private
21976     isNotifyTarget : true,
21977     
21978     /**
21979      * @hide
21980      */
21981     notifyEnter : function(dd, e, data)
21982     {
21983         this.valid = true;
21984         this.fireEvent('enter', dd, e, data);
21985         if(this.overClass){
21986             this.el.addClass(this.overClass);
21987         }
21988         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21989             this.valid ? this.dropAllowed : this.dropNotAllowed
21990         );
21991     },
21992
21993     /**
21994      * @hide
21995      */
21996     notifyOver : function(dd, e, data)
21997     {
21998         this.valid = true;
21999         this.fireEvent('over', dd, e, data);
22000         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22001             this.valid ? this.dropAllowed : this.dropNotAllowed
22002         );
22003     },
22004
22005     /**
22006      * @hide
22007      */
22008     notifyOut : function(dd, e, data)
22009     {
22010         this.fireEvent('out', dd, e, data);
22011         if(this.overClass){
22012             this.el.removeClass(this.overClass);
22013         }
22014     },
22015
22016     /**
22017      * @hide
22018      */
22019     notifyDrop : function(dd, e, data)
22020     {
22021         this.success = false;
22022         this.fireEvent('drop', dd, e, data);
22023         return this.success;
22024     }
22025 });/*
22026  * Based on:
22027  * Ext JS Library 1.1.1
22028  * Copyright(c) 2006-2007, Ext JS, LLC.
22029  *
22030  * Originally Released Under LGPL - original licence link has changed is not relivant.
22031  *
22032  * Fork - LGPL
22033  * <script type="text/javascript">
22034  */
22035
22036
22037 /**
22038  * @class Roo.dd.DragZone
22039  * @extends Roo.dd.DragSource
22040  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22041  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22042  * @constructor
22043  * @param {String/HTMLElement/Element} el The container element
22044  * @param {Object} config
22045  */
22046 Roo.dd.DragZone = function(el, config){
22047     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22048     if(this.containerScroll){
22049         Roo.dd.ScrollManager.register(this.el);
22050     }
22051 };
22052
22053 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22054     /**
22055      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22056      * for auto scrolling during drag operations.
22057      */
22058     /**
22059      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22060      * method after a failed drop (defaults to "c3daf9" - light blue)
22061      */
22062
22063     /**
22064      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22065      * for a valid target to drag based on the mouse down. Override this method
22066      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22067      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22068      * @param {EventObject} e The mouse down event
22069      * @return {Object} The dragData
22070      */
22071     getDragData : function(e){
22072         return Roo.dd.Registry.getHandleFromEvent(e);
22073     },
22074     
22075     /**
22076      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22077      * this.dragData.ddel
22078      * @param {Number} x The x position of the click on the dragged object
22079      * @param {Number} y The y position of the click on the dragged object
22080      * @return {Boolean} true to continue the drag, false to cancel
22081      */
22082     onInitDrag : function(x, y){
22083         this.proxy.update(this.dragData.ddel.cloneNode(true));
22084         this.onStartDrag(x, y);
22085         return true;
22086     },
22087     
22088     /**
22089      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22090      */
22091     afterRepair : function(){
22092         if(Roo.enableFx){
22093             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22094         }
22095         this.dragging = false;
22096     },
22097
22098     /**
22099      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22100      * the XY of this.dragData.ddel
22101      * @param {EventObject} e The mouse up event
22102      * @return {Array} The xy location (e.g. [100, 200])
22103      */
22104     getRepairXY : function(e){
22105         return Roo.Element.fly(this.dragData.ddel).getXY();  
22106     }
22107 });/*
22108  * Based on:
22109  * Ext JS Library 1.1.1
22110  * Copyright(c) 2006-2007, Ext JS, LLC.
22111  *
22112  * Originally Released Under LGPL - original licence link has changed is not relivant.
22113  *
22114  * Fork - LGPL
22115  * <script type="text/javascript">
22116  */
22117 /**
22118  * @class Roo.dd.DropZone
22119  * @extends Roo.dd.DropTarget
22120  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22121  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22122  * @constructor
22123  * @param {String/HTMLElement/Element} el The container element
22124  * @param {Object} config
22125  */
22126 Roo.dd.DropZone = function(el, config){
22127     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22128 };
22129
22130 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22131     /**
22132      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22133      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22134      * provide your own custom lookup.
22135      * @param {Event} e The event
22136      * @return {Object} data The custom data
22137      */
22138     getTargetFromEvent : function(e){
22139         return Roo.dd.Registry.getTargetFromEvent(e);
22140     },
22141
22142     /**
22143      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22144      * that it has registered.  This method has no default implementation and should be overridden to provide
22145      * node-specific processing if necessary.
22146      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22147      * {@link #getTargetFromEvent} for this node)
22148      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22149      * @param {Event} e The event
22150      * @param {Object} data An object containing arbitrary data supplied by the drag source
22151      */
22152     onNodeEnter : function(n, dd, e, data){
22153         
22154     },
22155
22156     /**
22157      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22158      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22159      * overridden to provide the proper feedback.
22160      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22161      * {@link #getTargetFromEvent} for this node)
22162      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22163      * @param {Event} e The event
22164      * @param {Object} data An object containing arbitrary data supplied by the drag source
22165      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22166      * underlying {@link Roo.dd.StatusProxy} can be updated
22167      */
22168     onNodeOver : function(n, dd, e, data){
22169         return this.dropAllowed;
22170     },
22171
22172     /**
22173      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22174      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22175      * node-specific processing if necessary.
22176      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22177      * {@link #getTargetFromEvent} for this node)
22178      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22179      * @param {Event} e The event
22180      * @param {Object} data An object containing arbitrary data supplied by the drag source
22181      */
22182     onNodeOut : function(n, dd, e, data){
22183         
22184     },
22185
22186     /**
22187      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22188      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22189      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22190      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22191      * {@link #getTargetFromEvent} for this node)
22192      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22193      * @param {Event} e The event
22194      * @param {Object} data An object containing arbitrary data supplied by the drag source
22195      * @return {Boolean} True if the drop was valid, else false
22196      */
22197     onNodeDrop : function(n, dd, e, data){
22198         return false;
22199     },
22200
22201     /**
22202      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22203      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22204      * it should be overridden to provide the proper feedback if necessary.
22205      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22206      * @param {Event} e The event
22207      * @param {Object} data An object containing arbitrary data supplied by the drag source
22208      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22209      * underlying {@link Roo.dd.StatusProxy} can be updated
22210      */
22211     onContainerOver : function(dd, e, data){
22212         return this.dropNotAllowed;
22213     },
22214
22215     /**
22216      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22217      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22218      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22219      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22220      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22221      * @param {Event} e The event
22222      * @param {Object} data An object containing arbitrary data supplied by the drag source
22223      * @return {Boolean} True if the drop was valid, else false
22224      */
22225     onContainerDrop : function(dd, e, data){
22226         return false;
22227     },
22228
22229     /**
22230      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22231      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22232      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22233      * you should override this method and provide a custom implementation.
22234      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22235      * @param {Event} e The event
22236      * @param {Object} data An object containing arbitrary data supplied by the drag source
22237      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22238      * underlying {@link Roo.dd.StatusProxy} can be updated
22239      */
22240     notifyEnter : function(dd, e, data){
22241         return this.dropNotAllowed;
22242     },
22243
22244     /**
22245      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22246      * This method will be called on every mouse movement while the drag source is over the drop zone.
22247      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22248      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22249      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22250      * registered node, it will call {@link #onContainerOver}.
22251      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22252      * @param {Event} e The event
22253      * @param {Object} data An object containing arbitrary data supplied by the drag source
22254      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22255      * underlying {@link Roo.dd.StatusProxy} can be updated
22256      */
22257     notifyOver : function(dd, e, data){
22258         var n = this.getTargetFromEvent(e);
22259         if(!n){ // not over valid drop target
22260             if(this.lastOverNode){
22261                 this.onNodeOut(this.lastOverNode, dd, e, data);
22262                 this.lastOverNode = null;
22263             }
22264             return this.onContainerOver(dd, e, data);
22265         }
22266         if(this.lastOverNode != n){
22267             if(this.lastOverNode){
22268                 this.onNodeOut(this.lastOverNode, dd, e, data);
22269             }
22270             this.onNodeEnter(n, dd, e, data);
22271             this.lastOverNode = n;
22272         }
22273         return this.onNodeOver(n, dd, e, data);
22274     },
22275
22276     /**
22277      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22278      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22279      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22280      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22281      * @param {Event} e The event
22282      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22283      */
22284     notifyOut : function(dd, e, data){
22285         if(this.lastOverNode){
22286             this.onNodeOut(this.lastOverNode, dd, e, data);
22287             this.lastOverNode = null;
22288         }
22289     },
22290
22291     /**
22292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22293      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22294      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22295      * otherwise it will call {@link #onContainerDrop}.
22296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22297      * @param {Event} e The event
22298      * @param {Object} data An object containing arbitrary data supplied by the drag source
22299      * @return {Boolean} True if the drop was valid, else false
22300      */
22301     notifyDrop : function(dd, e, data){
22302         if(this.lastOverNode){
22303             this.onNodeOut(this.lastOverNode, dd, e, data);
22304             this.lastOverNode = null;
22305         }
22306         var n = this.getTargetFromEvent(e);
22307         return n ?
22308             this.onNodeDrop(n, dd, e, data) :
22309             this.onContainerDrop(dd, e, data);
22310     },
22311
22312     // private
22313     triggerCacheRefresh : function(){
22314         Roo.dd.DDM.refreshCache(this.groups);
22315     }  
22316 });/*
22317  * Based on:
22318  * Ext JS Library 1.1.1
22319  * Copyright(c) 2006-2007, Ext JS, LLC.
22320  *
22321  * Originally Released Under LGPL - original licence link has changed is not relivant.
22322  *
22323  * Fork - LGPL
22324  * <script type="text/javascript">
22325  */
22326
22327
22328 /**
22329  * @class Roo.data.SortTypes
22330  * @singleton
22331  * Defines the default sorting (casting?) comparison functions used when sorting data.
22332  */
22333 Roo.data.SortTypes = {
22334     /**
22335      * Default sort that does nothing
22336      * @param {Mixed} s The value being converted
22337      * @return {Mixed} The comparison value
22338      */
22339     none : function(s){
22340         return s;
22341     },
22342     
22343     /**
22344      * The regular expression used to strip tags
22345      * @type {RegExp}
22346      * @property
22347      */
22348     stripTagsRE : /<\/?[^>]+>/gi,
22349     
22350     /**
22351      * Strips all HTML tags to sort on text only
22352      * @param {Mixed} s The value being converted
22353      * @return {String} The comparison value
22354      */
22355     asText : function(s){
22356         return String(s).replace(this.stripTagsRE, "");
22357     },
22358     
22359     /**
22360      * Strips all HTML tags to sort on text only - Case insensitive
22361      * @param {Mixed} s The value being converted
22362      * @return {String} The comparison value
22363      */
22364     asUCText : function(s){
22365         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22366     },
22367     
22368     /**
22369      * Case insensitive string
22370      * @param {Mixed} s The value being converted
22371      * @return {String} The comparison value
22372      */
22373     asUCString : function(s) {
22374         return String(s).toUpperCase();
22375     },
22376     
22377     /**
22378      * Date sorting
22379      * @param {Mixed} s The value being converted
22380      * @return {Number} The comparison value
22381      */
22382     asDate : function(s) {
22383         if(!s){
22384             return 0;
22385         }
22386         if(s instanceof Date){
22387             return s.getTime();
22388         }
22389         return Date.parse(String(s));
22390     },
22391     
22392     /**
22393      * Float sorting
22394      * @param {Mixed} s The value being converted
22395      * @return {Float} The comparison value
22396      */
22397     asFloat : function(s) {
22398         var val = parseFloat(String(s).replace(/,/g, ""));
22399         if(isNaN(val)) {
22400             val = 0;
22401         }
22402         return val;
22403     },
22404     
22405     /**
22406      * Integer sorting
22407      * @param {Mixed} s The value being converted
22408      * @return {Number} The comparison value
22409      */
22410     asInt : function(s) {
22411         var val = parseInt(String(s).replace(/,/g, ""));
22412         if(isNaN(val)) {
22413             val = 0;
22414         }
22415         return val;
22416     }
22417 };/*
22418  * Based on:
22419  * Ext JS Library 1.1.1
22420  * Copyright(c) 2006-2007, Ext JS, LLC.
22421  *
22422  * Originally Released Under LGPL - original licence link has changed is not relivant.
22423  *
22424  * Fork - LGPL
22425  * <script type="text/javascript">
22426  */
22427
22428 /**
22429 * @class Roo.data.Record
22430  * Instances of this class encapsulate both record <em>definition</em> information, and record
22431  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22432  * to access Records cached in an {@link Roo.data.Store} object.<br>
22433  * <p>
22434  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22435  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22436  * objects.<br>
22437  * <p>
22438  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22439  * @constructor
22440  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22441  * {@link #create}. The parameters are the same.
22442  * @param {Array} data An associative Array of data values keyed by the field name.
22443  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22444  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22445  * not specified an integer id is generated.
22446  */
22447 Roo.data.Record = function(data, id){
22448     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22449     this.data = data;
22450 };
22451
22452 /**
22453  * Generate a constructor for a specific record layout.
22454  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22455  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22456  * Each field definition object may contain the following properties: <ul>
22457  * <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,
22458  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22459  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22460  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22461  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22462  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22463  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22464  * this may be omitted.</p></li>
22465  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22466  * <ul><li>auto (Default, implies no conversion)</li>
22467  * <li>string</li>
22468  * <li>int</li>
22469  * <li>float</li>
22470  * <li>boolean</li>
22471  * <li>date</li></ul></p></li>
22472  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22473  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22474  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22475  * by the Reader into an object that will be stored in the Record. It is passed the
22476  * following parameters:<ul>
22477  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22478  * </ul></p></li>
22479  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22480  * </ul>
22481  * <br>usage:<br><pre><code>
22482 var TopicRecord = Roo.data.Record.create(
22483     {name: 'title', mapping: 'topic_title'},
22484     {name: 'author', mapping: 'username'},
22485     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22486     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22487     {name: 'lastPoster', mapping: 'user2'},
22488     {name: 'excerpt', mapping: 'post_text'}
22489 );
22490
22491 var myNewRecord = new TopicRecord({
22492     title: 'Do my job please',
22493     author: 'noobie',
22494     totalPosts: 1,
22495     lastPost: new Date(),
22496     lastPoster: 'Animal',
22497     excerpt: 'No way dude!'
22498 });
22499 myStore.add(myNewRecord);
22500 </code></pre>
22501  * @method create
22502  * @static
22503  */
22504 Roo.data.Record.create = function(o){
22505     var f = function(){
22506         f.superclass.constructor.apply(this, arguments);
22507     };
22508     Roo.extend(f, Roo.data.Record);
22509     var p = f.prototype;
22510     p.fields = new Roo.util.MixedCollection(false, function(field){
22511         return field.name;
22512     });
22513     for(var i = 0, len = o.length; i < len; i++){
22514         p.fields.add(new Roo.data.Field(o[i]));
22515     }
22516     f.getField = function(name){
22517         return p.fields.get(name);  
22518     };
22519     return f;
22520 };
22521
22522 Roo.data.Record.AUTO_ID = 1000;
22523 Roo.data.Record.EDIT = 'edit';
22524 Roo.data.Record.REJECT = 'reject';
22525 Roo.data.Record.COMMIT = 'commit';
22526
22527 Roo.data.Record.prototype = {
22528     /**
22529      * Readonly flag - true if this record has been modified.
22530      * @type Boolean
22531      */
22532     dirty : false,
22533     editing : false,
22534     error: null,
22535     modified: null,
22536
22537     // private
22538     join : function(store){
22539         this.store = store;
22540     },
22541
22542     /**
22543      * Set the named field to the specified value.
22544      * @param {String} name The name of the field to set.
22545      * @param {Object} value The value to set the field to.
22546      */
22547     set : function(name, value){
22548         if(this.data[name] == value){
22549             return;
22550         }
22551         this.dirty = true;
22552         if(!this.modified){
22553             this.modified = {};
22554         }
22555         if(typeof this.modified[name] == 'undefined'){
22556             this.modified[name] = this.data[name];
22557         }
22558         this.data[name] = value;
22559         if(!this.editing && this.store){
22560             this.store.afterEdit(this);
22561         }       
22562     },
22563
22564     /**
22565      * Get the value of the named field.
22566      * @param {String} name The name of the field to get the value of.
22567      * @return {Object} The value of the field.
22568      */
22569     get : function(name){
22570         return this.data[name]; 
22571     },
22572
22573     // private
22574     beginEdit : function(){
22575         this.editing = true;
22576         this.modified = {}; 
22577     },
22578
22579     // private
22580     cancelEdit : function(){
22581         this.editing = false;
22582         delete this.modified;
22583     },
22584
22585     // private
22586     endEdit : function(){
22587         this.editing = false;
22588         if(this.dirty && this.store){
22589             this.store.afterEdit(this);
22590         }
22591     },
22592
22593     /**
22594      * Usually called by the {@link Roo.data.Store} which owns the Record.
22595      * Rejects all changes made to the Record since either creation, or the last commit operation.
22596      * Modified fields are reverted to their original values.
22597      * <p>
22598      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22599      * of reject operations.
22600      */
22601     reject : function(){
22602         var m = this.modified;
22603         for(var n in m){
22604             if(typeof m[n] != "function"){
22605                 this.data[n] = m[n];
22606             }
22607         }
22608         this.dirty = false;
22609         delete this.modified;
22610         this.editing = false;
22611         if(this.store){
22612             this.store.afterReject(this);
22613         }
22614     },
22615
22616     /**
22617      * Usually called by the {@link Roo.data.Store} which owns the Record.
22618      * Commits all changes made to the Record since either creation, or the last commit operation.
22619      * <p>
22620      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22621      * of commit operations.
22622      */
22623     commit : function(){
22624         this.dirty = false;
22625         delete this.modified;
22626         this.editing = false;
22627         if(this.store){
22628             this.store.afterCommit(this);
22629         }
22630     },
22631
22632     // private
22633     hasError : function(){
22634         return this.error != null;
22635     },
22636
22637     // private
22638     clearError : function(){
22639         this.error = null;
22640     },
22641
22642     /**
22643      * Creates a copy of this record.
22644      * @param {String} id (optional) A new record id if you don't want to use this record's id
22645      * @return {Record}
22646      */
22647     copy : function(newId) {
22648         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22649     }
22650 };/*
22651  * Based on:
22652  * Ext JS Library 1.1.1
22653  * Copyright(c) 2006-2007, Ext JS, LLC.
22654  *
22655  * Originally Released Under LGPL - original licence link has changed is not relivant.
22656  *
22657  * Fork - LGPL
22658  * <script type="text/javascript">
22659  */
22660
22661
22662
22663 /**
22664  * @class Roo.data.Store
22665  * @extends Roo.util.Observable
22666  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22667  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22668  * <p>
22669  * 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
22670  * has no knowledge of the format of the data returned by the Proxy.<br>
22671  * <p>
22672  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22673  * instances from the data object. These records are cached and made available through accessor functions.
22674  * @constructor
22675  * Creates a new Store.
22676  * @param {Object} config A config object containing the objects needed for the Store to access data,
22677  * and read the data into Records.
22678  */
22679 Roo.data.Store = function(config){
22680     this.data = new Roo.util.MixedCollection(false);
22681     this.data.getKey = function(o){
22682         return o.id;
22683     };
22684     this.baseParams = {};
22685     // private
22686     this.paramNames = {
22687         "start" : "start",
22688         "limit" : "limit",
22689         "sort" : "sort",
22690         "dir" : "dir",
22691         "multisort" : "_multisort"
22692     };
22693
22694     if(config && config.data){
22695         this.inlineData = config.data;
22696         delete config.data;
22697     }
22698
22699     Roo.apply(this, config);
22700     
22701     if(this.reader){ // reader passed
22702         this.reader = Roo.factory(this.reader, Roo.data);
22703         this.reader.xmodule = this.xmodule || false;
22704         if(!this.recordType){
22705             this.recordType = this.reader.recordType;
22706         }
22707         if(this.reader.onMetaChange){
22708             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22709         }
22710     }
22711
22712     if(this.recordType){
22713         this.fields = this.recordType.prototype.fields;
22714     }
22715     this.modified = [];
22716
22717     this.addEvents({
22718         /**
22719          * @event datachanged
22720          * Fires when the data cache has changed, and a widget which is using this Store
22721          * as a Record cache should refresh its view.
22722          * @param {Store} this
22723          */
22724         datachanged : true,
22725         /**
22726          * @event metachange
22727          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22728          * @param {Store} this
22729          * @param {Object} meta The JSON metadata
22730          */
22731         metachange : true,
22732         /**
22733          * @event add
22734          * Fires when Records have been added to the Store
22735          * @param {Store} this
22736          * @param {Roo.data.Record[]} records The array of Records added
22737          * @param {Number} index The index at which the record(s) were added
22738          */
22739         add : true,
22740         /**
22741          * @event remove
22742          * Fires when a Record has been removed from the Store
22743          * @param {Store} this
22744          * @param {Roo.data.Record} record The Record that was removed
22745          * @param {Number} index The index at which the record was removed
22746          */
22747         remove : true,
22748         /**
22749          * @event update
22750          * Fires when a Record has been updated
22751          * @param {Store} this
22752          * @param {Roo.data.Record} record The Record that was updated
22753          * @param {String} operation The update operation being performed.  Value may be one of:
22754          * <pre><code>
22755  Roo.data.Record.EDIT
22756  Roo.data.Record.REJECT
22757  Roo.data.Record.COMMIT
22758          * </code></pre>
22759          */
22760         update : true,
22761         /**
22762          * @event clear
22763          * Fires when the data cache has been cleared.
22764          * @param {Store} this
22765          */
22766         clear : true,
22767         /**
22768          * @event beforeload
22769          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22770          * the load action will be canceled.
22771          * @param {Store} this
22772          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22773          */
22774         beforeload : true,
22775         /**
22776          * @event beforeloadadd
22777          * Fires after a new set of Records has been loaded.
22778          * @param {Store} this
22779          * @param {Roo.data.Record[]} records The Records that were loaded
22780          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22781          */
22782         beforeloadadd : true,
22783         /**
22784          * @event load
22785          * Fires after a new set of Records has been loaded, before they are added to the store.
22786          * @param {Store} this
22787          * @param {Roo.data.Record[]} records The Records that were loaded
22788          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22789          * @params {Object} return from reader
22790          */
22791         load : true,
22792         /**
22793          * @event loadexception
22794          * Fires if an exception occurs in the Proxy during loading.
22795          * Called with the signature of the Proxy's "loadexception" event.
22796          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22797          * 
22798          * @param {Proxy} 
22799          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22800          * @param {Object} load options 
22801          * @param {Object} jsonData from your request (normally this contains the Exception)
22802          */
22803         loadexception : true
22804     });
22805     
22806     if(this.proxy){
22807         this.proxy = Roo.factory(this.proxy, Roo.data);
22808         this.proxy.xmodule = this.xmodule || false;
22809         this.relayEvents(this.proxy,  ["loadexception"]);
22810     }
22811     this.sortToggle = {};
22812     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22813
22814     Roo.data.Store.superclass.constructor.call(this);
22815
22816     if(this.inlineData){
22817         this.loadData(this.inlineData);
22818         delete this.inlineData;
22819     }
22820 };
22821
22822 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22823      /**
22824     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22825     * without a remote query - used by combo/forms at present.
22826     */
22827     
22828     /**
22829     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22830     */
22831     /**
22832     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22833     */
22834     /**
22835     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22836     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22837     */
22838     /**
22839     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22840     * on any HTTP request
22841     */
22842     /**
22843     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22844     */
22845     /**
22846     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22847     */
22848     multiSort: false,
22849     /**
22850     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22851     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22852     */
22853     remoteSort : false,
22854
22855     /**
22856     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22857      * loaded or when a record is removed. (defaults to false).
22858     */
22859     pruneModifiedRecords : false,
22860
22861     // private
22862     lastOptions : null,
22863
22864     /**
22865      * Add Records to the Store and fires the add event.
22866      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22867      */
22868     add : function(records){
22869         records = [].concat(records);
22870         for(var i = 0, len = records.length; i < len; i++){
22871             records[i].join(this);
22872         }
22873         var index = this.data.length;
22874         this.data.addAll(records);
22875         this.fireEvent("add", this, records, index);
22876     },
22877
22878     /**
22879      * Remove a Record from the Store and fires the remove event.
22880      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22881      */
22882     remove : function(record){
22883         var index = this.data.indexOf(record);
22884         this.data.removeAt(index);
22885         if(this.pruneModifiedRecords){
22886             this.modified.remove(record);
22887         }
22888         this.fireEvent("remove", this, record, index);
22889     },
22890
22891     /**
22892      * Remove all Records from the Store and fires the clear event.
22893      */
22894     removeAll : function(){
22895         this.data.clear();
22896         if(this.pruneModifiedRecords){
22897             this.modified = [];
22898         }
22899         this.fireEvent("clear", this);
22900     },
22901
22902     /**
22903      * Inserts Records to the Store at the given index and fires the add event.
22904      * @param {Number} index The start index at which to insert the passed Records.
22905      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22906      */
22907     insert : function(index, records){
22908         records = [].concat(records);
22909         for(var i = 0, len = records.length; i < len; i++){
22910             this.data.insert(index, records[i]);
22911             records[i].join(this);
22912         }
22913         this.fireEvent("add", this, records, index);
22914     },
22915
22916     /**
22917      * Get the index within the cache of the passed Record.
22918      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22919      * @return {Number} The index of the passed Record. Returns -1 if not found.
22920      */
22921     indexOf : function(record){
22922         return this.data.indexOf(record);
22923     },
22924
22925     /**
22926      * Get the index within the cache of the Record with the passed id.
22927      * @param {String} id The id of the Record to find.
22928      * @return {Number} The index of the Record. Returns -1 if not found.
22929      */
22930     indexOfId : function(id){
22931         return this.data.indexOfKey(id);
22932     },
22933
22934     /**
22935      * Get the Record with the specified id.
22936      * @param {String} id The id of the Record to find.
22937      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22938      */
22939     getById : function(id){
22940         return this.data.key(id);
22941     },
22942
22943     /**
22944      * Get the Record at the specified index.
22945      * @param {Number} index The index of the Record to find.
22946      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22947      */
22948     getAt : function(index){
22949         return this.data.itemAt(index);
22950     },
22951
22952     /**
22953      * Returns a range of Records between specified indices.
22954      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22955      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22956      * @return {Roo.data.Record[]} An array of Records
22957      */
22958     getRange : function(start, end){
22959         return this.data.getRange(start, end);
22960     },
22961
22962     // private
22963     storeOptions : function(o){
22964         o = Roo.apply({}, o);
22965         delete o.callback;
22966         delete o.scope;
22967         this.lastOptions = o;
22968     },
22969
22970     /**
22971      * Loads the Record cache from the configured Proxy using the configured Reader.
22972      * <p>
22973      * If using remote paging, then the first load call must specify the <em>start</em>
22974      * and <em>limit</em> properties in the options.params property to establish the initial
22975      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22976      * <p>
22977      * <strong>It is important to note that for remote data sources, loading is asynchronous,
22978      * and this call will return before the new data has been loaded. Perform any post-processing
22979      * in a callback function, or in a "load" event handler.</strong>
22980      * <p>
22981      * @param {Object} options An object containing properties which control loading options:<ul>
22982      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
22983      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
22984      * passed the following arguments:<ul>
22985      * <li>r : Roo.data.Record[]</li>
22986      * <li>options: Options object from the load call</li>
22987      * <li>success: Boolean success indicator</li></ul></li>
22988      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
22989      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
22990      * </ul>
22991      */
22992     load : function(options){
22993         options = options || {};
22994         if(this.fireEvent("beforeload", this, options) !== false){
22995             this.storeOptions(options);
22996             var p = Roo.apply(options.params || {}, this.baseParams);
22997             // if meta was not loaded from remote source.. try requesting it.
22998             if (!this.reader.metaFromRemote) {
22999                 p._requestMeta = 1;
23000             }
23001             if(this.sortInfo && this.remoteSort){
23002                 var pn = this.paramNames;
23003                 p[pn["sort"]] = this.sortInfo.field;
23004                 p[pn["dir"]] = this.sortInfo.direction;
23005             }
23006             if (this.multiSort) {
23007                 var pn = this.paramNames;
23008                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23009             }
23010             
23011             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23012         }
23013     },
23014
23015     /**
23016      * Reloads the Record cache from the configured Proxy using the configured Reader and
23017      * the options from the last load operation performed.
23018      * @param {Object} options (optional) An object containing properties which may override the options
23019      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23020      * the most recently used options are reused).
23021      */
23022     reload : function(options){
23023         this.load(Roo.applyIf(options||{}, this.lastOptions));
23024     },
23025
23026     // private
23027     // Called as a callback by the Reader during a load operation.
23028     loadRecords : function(o, options, success){
23029         if(!o || success === false){
23030             if(success !== false){
23031                 this.fireEvent("load", this, [], options, o);
23032             }
23033             if(options.callback){
23034                 options.callback.call(options.scope || this, [], options, false);
23035             }
23036             return;
23037         }
23038         // if data returned failure - throw an exception.
23039         if (o.success === false) {
23040             // show a message if no listener is registered.
23041             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23042                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23043             }
23044             // loadmask wil be hooked into this..
23045             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23046             return;
23047         }
23048         var r = o.records, t = o.totalRecords || r.length;
23049         
23050         this.fireEvent("beforeloadadd", this, r, options, o);
23051         
23052         if(!options || options.add !== true){
23053             if(this.pruneModifiedRecords){
23054                 this.modified = [];
23055             }
23056             for(var i = 0, len = r.length; i < len; i++){
23057                 r[i].join(this);
23058             }
23059             if(this.snapshot){
23060                 this.data = this.snapshot;
23061                 delete this.snapshot;
23062             }
23063             this.data.clear();
23064             this.data.addAll(r);
23065             this.totalLength = t;
23066             this.applySort();
23067             this.fireEvent("datachanged", this);
23068         }else{
23069             this.totalLength = Math.max(t, this.data.length+r.length);
23070             this.add(r);
23071         }
23072         this.fireEvent("load", this, r, options, o);
23073         if(options.callback){
23074             options.callback.call(options.scope || this, r, options, true);
23075         }
23076     },
23077
23078
23079     /**
23080      * Loads data from a passed data block. A Reader which understands the format of the data
23081      * must have been configured in the constructor.
23082      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23083      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23084      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23085      */
23086     loadData : function(o, append){
23087         var r = this.reader.readRecords(o);
23088         this.loadRecords(r, {add: append}, true);
23089     },
23090
23091     /**
23092      * Gets the number of cached records.
23093      * <p>
23094      * <em>If using paging, this may not be the total size of the dataset. If the data object
23095      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23096      * the data set size</em>
23097      */
23098     getCount : function(){
23099         return this.data.length || 0;
23100     },
23101
23102     /**
23103      * Gets the total number of records in the dataset as returned by the server.
23104      * <p>
23105      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23106      * the dataset size</em>
23107      */
23108     getTotalCount : function(){
23109         return this.totalLength || 0;
23110     },
23111
23112     /**
23113      * Returns the sort state of the Store as an object with two properties:
23114      * <pre><code>
23115  field {String} The name of the field by which the Records are sorted
23116  direction {String} The sort order, "ASC" or "DESC"
23117      * </code></pre>
23118      */
23119     getSortState : function(){
23120         return this.sortInfo;
23121     },
23122
23123     // private
23124     applySort : function(){
23125         if(this.sortInfo && !this.remoteSort){
23126             var s = this.sortInfo, f = s.field;
23127             var st = this.fields.get(f).sortType;
23128             var fn = function(r1, r2){
23129                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23130                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23131             };
23132             this.data.sort(s.direction, fn);
23133             if(this.snapshot && this.snapshot != this.data){
23134                 this.snapshot.sort(s.direction, fn);
23135             }
23136         }
23137     },
23138
23139     /**
23140      * Sets the default sort column and order to be used by the next load operation.
23141      * @param {String} fieldName The name of the field to sort by.
23142      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23143      */
23144     setDefaultSort : function(field, dir){
23145         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23146     },
23147
23148     /**
23149      * Sort the Records.
23150      * If remote sorting is used, the sort is performed on the server, and the cache is
23151      * reloaded. If local sorting is used, the cache is sorted internally.
23152      * @param {String} fieldName The name of the field to sort by.
23153      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23154      */
23155     sort : function(fieldName, dir){
23156         var f = this.fields.get(fieldName);
23157         if(!dir){
23158             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23159             
23160             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23161                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23162             }else{
23163                 dir = f.sortDir;
23164             }
23165         }
23166         this.sortToggle[f.name] = dir;
23167         this.sortInfo = {field: f.name, direction: dir};
23168         if(!this.remoteSort){
23169             this.applySort();
23170             this.fireEvent("datachanged", this);
23171         }else{
23172             this.load(this.lastOptions);
23173         }
23174     },
23175
23176     /**
23177      * Calls the specified function for each of the Records in the cache.
23178      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23179      * Returning <em>false</em> aborts and exits the iteration.
23180      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23181      */
23182     each : function(fn, scope){
23183         this.data.each(fn, scope);
23184     },
23185
23186     /**
23187      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23188      * (e.g., during paging).
23189      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23190      */
23191     getModifiedRecords : function(){
23192         return this.modified;
23193     },
23194
23195     // private
23196     createFilterFn : function(property, value, anyMatch){
23197         if(!value.exec){ // not a regex
23198             value = String(value);
23199             if(value.length == 0){
23200                 return false;
23201             }
23202             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23203         }
23204         return function(r){
23205             return value.test(r.data[property]);
23206         };
23207     },
23208
23209     /**
23210      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23211      * @param {String} property A field on your records
23212      * @param {Number} start The record index to start at (defaults to 0)
23213      * @param {Number} end The last record index to include (defaults to length - 1)
23214      * @return {Number} The sum
23215      */
23216     sum : function(property, start, end){
23217         var rs = this.data.items, v = 0;
23218         start = start || 0;
23219         end = (end || end === 0) ? end : rs.length-1;
23220
23221         for(var i = start; i <= end; i++){
23222             v += (rs[i].data[property] || 0);
23223         }
23224         return v;
23225     },
23226
23227     /**
23228      * Filter the records by a specified property.
23229      * @param {String} field A field on your records
23230      * @param {String/RegExp} value Either a string that the field
23231      * should start with or a RegExp to test against the field
23232      * @param {Boolean} anyMatch True to match any part not just the beginning
23233      */
23234     filter : function(property, value, anyMatch){
23235         var fn = this.createFilterFn(property, value, anyMatch);
23236         return fn ? this.filterBy(fn) : this.clearFilter();
23237     },
23238
23239     /**
23240      * Filter by a function. The specified function will be called with each
23241      * record in this data source. If the function returns true the record is included,
23242      * otherwise it is filtered.
23243      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23244      * @param {Object} scope (optional) The scope of the function (defaults to this)
23245      */
23246     filterBy : function(fn, scope){
23247         this.snapshot = this.snapshot || this.data;
23248         this.data = this.queryBy(fn, scope||this);
23249         this.fireEvent("datachanged", this);
23250     },
23251
23252     /**
23253      * Query the records by a specified property.
23254      * @param {String} field A field on your records
23255      * @param {String/RegExp} value Either a string that the field
23256      * should start with or a RegExp to test against the field
23257      * @param {Boolean} anyMatch True to match any part not just the beginning
23258      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23259      */
23260     query : function(property, value, anyMatch){
23261         var fn = this.createFilterFn(property, value, anyMatch);
23262         return fn ? this.queryBy(fn) : this.data.clone();
23263     },
23264
23265     /**
23266      * Query by a function. The specified function will be called with each
23267      * record in this data source. If the function returns true the record is included
23268      * in the results.
23269      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23270      * @param {Object} scope (optional) The scope of the function (defaults to this)
23271       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23272      **/
23273     queryBy : function(fn, scope){
23274         var data = this.snapshot || this.data;
23275         return data.filterBy(fn, scope||this);
23276     },
23277
23278     /**
23279      * Collects unique values for a particular dataIndex from this store.
23280      * @param {String} dataIndex The property to collect
23281      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23282      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23283      * @return {Array} An array of the unique values
23284      **/
23285     collect : function(dataIndex, allowNull, bypassFilter){
23286         var d = (bypassFilter === true && this.snapshot) ?
23287                 this.snapshot.items : this.data.items;
23288         var v, sv, r = [], l = {};
23289         for(var i = 0, len = d.length; i < len; i++){
23290             v = d[i].data[dataIndex];
23291             sv = String(v);
23292             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23293                 l[sv] = true;
23294                 r[r.length] = v;
23295             }
23296         }
23297         return r;
23298     },
23299
23300     /**
23301      * Revert to a view of the Record cache with no filtering applied.
23302      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23303      */
23304     clearFilter : function(suppressEvent){
23305         if(this.snapshot && this.snapshot != this.data){
23306             this.data = this.snapshot;
23307             delete this.snapshot;
23308             if(suppressEvent !== true){
23309                 this.fireEvent("datachanged", this);
23310             }
23311         }
23312     },
23313
23314     // private
23315     afterEdit : function(record){
23316         if(this.modified.indexOf(record) == -1){
23317             this.modified.push(record);
23318         }
23319         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23320     },
23321     
23322     // private
23323     afterReject : function(record){
23324         this.modified.remove(record);
23325         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23326     },
23327
23328     // private
23329     afterCommit : function(record){
23330         this.modified.remove(record);
23331         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23332     },
23333
23334     /**
23335      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23336      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23337      */
23338     commitChanges : function(){
23339         var m = this.modified.slice(0);
23340         this.modified = [];
23341         for(var i = 0, len = m.length; i < len; i++){
23342             m[i].commit();
23343         }
23344     },
23345
23346     /**
23347      * Cancel outstanding changes on all changed records.
23348      */
23349     rejectChanges : function(){
23350         var m = this.modified.slice(0);
23351         this.modified = [];
23352         for(var i = 0, len = m.length; i < len; i++){
23353             m[i].reject();
23354         }
23355     },
23356
23357     onMetaChange : function(meta, rtype, o){
23358         this.recordType = rtype;
23359         this.fields = rtype.prototype.fields;
23360         delete this.snapshot;
23361         this.sortInfo = meta.sortInfo || this.sortInfo;
23362         this.modified = [];
23363         this.fireEvent('metachange', this, this.reader.meta);
23364     },
23365     
23366     moveIndex : function(data, type)
23367     {
23368         var index = this.indexOf(data);
23369         
23370         var newIndex = index + type;
23371         
23372         this.remove(data);
23373         
23374         this.insert(newIndex, data);
23375         
23376     }
23377 });/*
23378  * Based on:
23379  * Ext JS Library 1.1.1
23380  * Copyright(c) 2006-2007, Ext JS, LLC.
23381  *
23382  * Originally Released Under LGPL - original licence link has changed is not relivant.
23383  *
23384  * Fork - LGPL
23385  * <script type="text/javascript">
23386  */
23387
23388 /**
23389  * @class Roo.data.SimpleStore
23390  * @extends Roo.data.Store
23391  * Small helper class to make creating Stores from Array data easier.
23392  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23393  * @cfg {Array} fields An array of field definition objects, or field name strings.
23394  * @cfg {Array} data The multi-dimensional array of data
23395  * @constructor
23396  * @param {Object} config
23397  */
23398 Roo.data.SimpleStore = function(config){
23399     Roo.data.SimpleStore.superclass.constructor.call(this, {
23400         isLocal : true,
23401         reader: new Roo.data.ArrayReader({
23402                 id: config.id
23403             },
23404             Roo.data.Record.create(config.fields)
23405         ),
23406         proxy : new Roo.data.MemoryProxy(config.data)
23407     });
23408     this.load();
23409 };
23410 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23411  * Based on:
23412  * Ext JS Library 1.1.1
23413  * Copyright(c) 2006-2007, Ext JS, LLC.
23414  *
23415  * Originally Released Under LGPL - original licence link has changed is not relivant.
23416  *
23417  * Fork - LGPL
23418  * <script type="text/javascript">
23419  */
23420
23421 /**
23422 /**
23423  * @extends Roo.data.Store
23424  * @class Roo.data.JsonStore
23425  * Small helper class to make creating Stores for JSON data easier. <br/>
23426 <pre><code>
23427 var store = new Roo.data.JsonStore({
23428     url: 'get-images.php',
23429     root: 'images',
23430     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23431 });
23432 </code></pre>
23433  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23434  * JsonReader and HttpProxy (unless inline data is provided).</b>
23435  * @cfg {Array} fields An array of field definition objects, or field name strings.
23436  * @constructor
23437  * @param {Object} config
23438  */
23439 Roo.data.JsonStore = function(c){
23440     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23441         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23442         reader: new Roo.data.JsonReader(c, c.fields)
23443     }));
23444 };
23445 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23446  * Based on:
23447  * Ext JS Library 1.1.1
23448  * Copyright(c) 2006-2007, Ext JS, LLC.
23449  *
23450  * Originally Released Under LGPL - original licence link has changed is not relivant.
23451  *
23452  * Fork - LGPL
23453  * <script type="text/javascript">
23454  */
23455
23456  
23457 Roo.data.Field = function(config){
23458     if(typeof config == "string"){
23459         config = {name: config};
23460     }
23461     Roo.apply(this, config);
23462     
23463     if(!this.type){
23464         this.type = "auto";
23465     }
23466     
23467     var st = Roo.data.SortTypes;
23468     // named sortTypes are supported, here we look them up
23469     if(typeof this.sortType == "string"){
23470         this.sortType = st[this.sortType];
23471     }
23472     
23473     // set default sortType for strings and dates
23474     if(!this.sortType){
23475         switch(this.type){
23476             case "string":
23477                 this.sortType = st.asUCString;
23478                 break;
23479             case "date":
23480                 this.sortType = st.asDate;
23481                 break;
23482             default:
23483                 this.sortType = st.none;
23484         }
23485     }
23486
23487     // define once
23488     var stripRe = /[\$,%]/g;
23489
23490     // prebuilt conversion function for this field, instead of
23491     // switching every time we're reading a value
23492     if(!this.convert){
23493         var cv, dateFormat = this.dateFormat;
23494         switch(this.type){
23495             case "":
23496             case "auto":
23497             case undefined:
23498                 cv = function(v){ return v; };
23499                 break;
23500             case "string":
23501                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23502                 break;
23503             case "int":
23504                 cv = function(v){
23505                     return v !== undefined && v !== null && v !== '' ?
23506                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23507                     };
23508                 break;
23509             case "float":
23510                 cv = function(v){
23511                     return v !== undefined && v !== null && v !== '' ?
23512                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23513                     };
23514                 break;
23515             case "bool":
23516             case "boolean":
23517                 cv = function(v){ return v === true || v === "true" || v == 1; };
23518                 break;
23519             case "date":
23520                 cv = function(v){
23521                     if(!v){
23522                         return '';
23523                     }
23524                     if(v instanceof Date){
23525                         return v;
23526                     }
23527                     if(dateFormat){
23528                         if(dateFormat == "timestamp"){
23529                             return new Date(v*1000);
23530                         }
23531                         return Date.parseDate(v, dateFormat);
23532                     }
23533                     var parsed = Date.parse(v);
23534                     return parsed ? new Date(parsed) : null;
23535                 };
23536              break;
23537             
23538         }
23539         this.convert = cv;
23540     }
23541 };
23542
23543 Roo.data.Field.prototype = {
23544     dateFormat: null,
23545     defaultValue: "",
23546     mapping: null,
23547     sortType : null,
23548     sortDir : "ASC"
23549 };/*
23550  * Based on:
23551  * Ext JS Library 1.1.1
23552  * Copyright(c) 2006-2007, Ext JS, LLC.
23553  *
23554  * Originally Released Under LGPL - original licence link has changed is not relivant.
23555  *
23556  * Fork - LGPL
23557  * <script type="text/javascript">
23558  */
23559  
23560 // Base class for reading structured data from a data source.  This class is intended to be
23561 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23562
23563 /**
23564  * @class Roo.data.DataReader
23565  * Base class for reading structured data from a data source.  This class is intended to be
23566  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23567  */
23568
23569 Roo.data.DataReader = function(meta, recordType){
23570     
23571     this.meta = meta;
23572     
23573     this.recordType = recordType instanceof Array ? 
23574         Roo.data.Record.create(recordType) : recordType;
23575 };
23576
23577 Roo.data.DataReader.prototype = {
23578      /**
23579      * Create an empty record
23580      * @param {Object} data (optional) - overlay some values
23581      * @return {Roo.data.Record} record created.
23582      */
23583     newRow :  function(d) {
23584         var da =  {};
23585         this.recordType.prototype.fields.each(function(c) {
23586             switch( c.type) {
23587                 case 'int' : da[c.name] = 0; break;
23588                 case 'date' : da[c.name] = new Date(); break;
23589                 case 'float' : da[c.name] = 0.0; break;
23590                 case 'boolean' : da[c.name] = false; break;
23591                 default : da[c.name] = ""; break;
23592             }
23593             
23594         });
23595         return new this.recordType(Roo.apply(da, d));
23596     }
23597     
23598 };/*
23599  * Based on:
23600  * Ext JS Library 1.1.1
23601  * Copyright(c) 2006-2007, Ext JS, LLC.
23602  *
23603  * Originally Released Under LGPL - original licence link has changed is not relivant.
23604  *
23605  * Fork - LGPL
23606  * <script type="text/javascript">
23607  */
23608
23609 /**
23610  * @class Roo.data.DataProxy
23611  * @extends Roo.data.Observable
23612  * This class is an abstract base class for implementations which provide retrieval of
23613  * unformatted data objects.<br>
23614  * <p>
23615  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23616  * (of the appropriate type which knows how to parse the data object) to provide a block of
23617  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23618  * <p>
23619  * Custom implementations must implement the load method as described in
23620  * {@link Roo.data.HttpProxy#load}.
23621  */
23622 Roo.data.DataProxy = function(){
23623     this.addEvents({
23624         /**
23625          * @event beforeload
23626          * Fires before a network request is made to retrieve a data object.
23627          * @param {Object} This DataProxy object.
23628          * @param {Object} params The params parameter to the load function.
23629          */
23630         beforeload : true,
23631         /**
23632          * @event load
23633          * Fires before the load method's callback is called.
23634          * @param {Object} This DataProxy object.
23635          * @param {Object} o The data object.
23636          * @param {Object} arg The callback argument object passed to the load function.
23637          */
23638         load : true,
23639         /**
23640          * @event loadexception
23641          * Fires if an Exception occurs during data retrieval.
23642          * @param {Object} This DataProxy object.
23643          * @param {Object} o The data object.
23644          * @param {Object} arg The callback argument object passed to the load function.
23645          * @param {Object} e The Exception.
23646          */
23647         loadexception : true
23648     });
23649     Roo.data.DataProxy.superclass.constructor.call(this);
23650 };
23651
23652 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23653
23654     /**
23655      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23656      */
23657 /*
23658  * Based on:
23659  * Ext JS Library 1.1.1
23660  * Copyright(c) 2006-2007, Ext JS, LLC.
23661  *
23662  * Originally Released Under LGPL - original licence link has changed is not relivant.
23663  *
23664  * Fork - LGPL
23665  * <script type="text/javascript">
23666  */
23667 /**
23668  * @class Roo.data.MemoryProxy
23669  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23670  * to the Reader when its load method is called.
23671  * @constructor
23672  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23673  */
23674 Roo.data.MemoryProxy = function(data){
23675     if (data.data) {
23676         data = data.data;
23677     }
23678     Roo.data.MemoryProxy.superclass.constructor.call(this);
23679     this.data = data;
23680 };
23681
23682 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23683     
23684     /**
23685      * Load data from the requested source (in this case an in-memory
23686      * data object passed to the constructor), read the data object into
23687      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23688      * process that block using the passed callback.
23689      * @param {Object} params This parameter is not used by the MemoryProxy class.
23690      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23691      * object into a block of Roo.data.Records.
23692      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23693      * The function must be passed <ul>
23694      * <li>The Record block object</li>
23695      * <li>The "arg" argument from the load function</li>
23696      * <li>A boolean success indicator</li>
23697      * </ul>
23698      * @param {Object} scope The scope in which to call the callback
23699      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23700      */
23701     load : function(params, reader, callback, scope, arg){
23702         params = params || {};
23703         var result;
23704         try {
23705             result = reader.readRecords(this.data);
23706         }catch(e){
23707             this.fireEvent("loadexception", this, arg, null, e);
23708             callback.call(scope, null, arg, false);
23709             return;
23710         }
23711         callback.call(scope, result, arg, true);
23712     },
23713     
23714     // private
23715     update : function(params, records){
23716         
23717     }
23718 });/*
23719  * Based on:
23720  * Ext JS Library 1.1.1
23721  * Copyright(c) 2006-2007, Ext JS, LLC.
23722  *
23723  * Originally Released Under LGPL - original licence link has changed is not relivant.
23724  *
23725  * Fork - LGPL
23726  * <script type="text/javascript">
23727  */
23728 /**
23729  * @class Roo.data.HttpProxy
23730  * @extends Roo.data.DataProxy
23731  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23732  * configured to reference a certain URL.<br><br>
23733  * <p>
23734  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23735  * from which the running page was served.<br><br>
23736  * <p>
23737  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23738  * <p>
23739  * Be aware that to enable the browser to parse an XML document, the server must set
23740  * the Content-Type header in the HTTP response to "text/xml".
23741  * @constructor
23742  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23743  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23744  * will be used to make the request.
23745  */
23746 Roo.data.HttpProxy = function(conn){
23747     Roo.data.HttpProxy.superclass.constructor.call(this);
23748     // is conn a conn config or a real conn?
23749     this.conn = conn;
23750     this.useAjax = !conn || !conn.events;
23751   
23752 };
23753
23754 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23755     // thse are take from connection...
23756     
23757     /**
23758      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23759      */
23760     /**
23761      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23762      * extra parameters to each request made by this object. (defaults to undefined)
23763      */
23764     /**
23765      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23766      *  to each request made by this object. (defaults to undefined)
23767      */
23768     /**
23769      * @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)
23770      */
23771     /**
23772      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23773      */
23774      /**
23775      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23776      * @type Boolean
23777      */
23778   
23779
23780     /**
23781      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23782      * @type Boolean
23783      */
23784     /**
23785      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23786      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23787      * a finer-grained basis than the DataProxy events.
23788      */
23789     getConnection : function(){
23790         return this.useAjax ? Roo.Ajax : this.conn;
23791     },
23792
23793     /**
23794      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23795      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23796      * process that block using the passed callback.
23797      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23798      * for the request to the remote server.
23799      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23800      * object into a block of Roo.data.Records.
23801      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23802      * The function must be passed <ul>
23803      * <li>The Record block object</li>
23804      * <li>The "arg" argument from the load function</li>
23805      * <li>A boolean success indicator</li>
23806      * </ul>
23807      * @param {Object} scope The scope in which to call the callback
23808      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23809      */
23810     load : function(params, reader, callback, scope, arg){
23811         if(this.fireEvent("beforeload", this, params) !== false){
23812             var  o = {
23813                 params : params || {},
23814                 request: {
23815                     callback : callback,
23816                     scope : scope,
23817                     arg : arg
23818                 },
23819                 reader: reader,
23820                 callback : this.loadResponse,
23821                 scope: this
23822             };
23823             if(this.useAjax){
23824                 Roo.applyIf(o, this.conn);
23825                 if(this.activeRequest){
23826                     Roo.Ajax.abort(this.activeRequest);
23827                 }
23828                 this.activeRequest = Roo.Ajax.request(o);
23829             }else{
23830                 this.conn.request(o);
23831             }
23832         }else{
23833             callback.call(scope||this, null, arg, false);
23834         }
23835     },
23836
23837     // private
23838     loadResponse : function(o, success, response){
23839         delete this.activeRequest;
23840         if(!success){
23841             this.fireEvent("loadexception", this, o, response);
23842             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23843             return;
23844         }
23845         var result;
23846         try {
23847             result = o.reader.read(response);
23848         }catch(e){
23849             this.fireEvent("loadexception", this, o, response, e);
23850             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23851             return;
23852         }
23853         
23854         this.fireEvent("load", this, o, o.request.arg);
23855         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23856     },
23857
23858     // private
23859     update : function(dataSet){
23860
23861     },
23862
23863     // private
23864     updateResponse : function(dataSet){
23865
23866     }
23867 });/*
23868  * Based on:
23869  * Ext JS Library 1.1.1
23870  * Copyright(c) 2006-2007, Ext JS, LLC.
23871  *
23872  * Originally Released Under LGPL - original licence link has changed is not relivant.
23873  *
23874  * Fork - LGPL
23875  * <script type="text/javascript">
23876  */
23877
23878 /**
23879  * @class Roo.data.ScriptTagProxy
23880  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23881  * other than the originating domain of the running page.<br><br>
23882  * <p>
23883  * <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
23884  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23885  * <p>
23886  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23887  * source code that is used as the source inside a &lt;script> tag.<br><br>
23888  * <p>
23889  * In order for the browser to process the returned data, the server must wrap the data object
23890  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23891  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23892  * depending on whether the callback name was passed:
23893  * <p>
23894  * <pre><code>
23895 boolean scriptTag = false;
23896 String cb = request.getParameter("callback");
23897 if (cb != null) {
23898     scriptTag = true;
23899     response.setContentType("text/javascript");
23900 } else {
23901     response.setContentType("application/x-json");
23902 }
23903 Writer out = response.getWriter();
23904 if (scriptTag) {
23905     out.write(cb + "(");
23906 }
23907 out.print(dataBlock.toJsonString());
23908 if (scriptTag) {
23909     out.write(");");
23910 }
23911 </pre></code>
23912  *
23913  * @constructor
23914  * @param {Object} config A configuration object.
23915  */
23916 Roo.data.ScriptTagProxy = function(config){
23917     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23918     Roo.apply(this, config);
23919     this.head = document.getElementsByTagName("head")[0];
23920 };
23921
23922 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23923
23924 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23925     /**
23926      * @cfg {String} url The URL from which to request the data object.
23927      */
23928     /**
23929      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23930      */
23931     timeout : 30000,
23932     /**
23933      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23934      * the server the name of the callback function set up by the load call to process the returned data object.
23935      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23936      * javascript output which calls this named function passing the data object as its only parameter.
23937      */
23938     callbackParam : "callback",
23939     /**
23940      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23941      * name to the request.
23942      */
23943     nocache : true,
23944
23945     /**
23946      * Load data from the configured URL, read the data object into
23947      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23948      * process that block using the passed callback.
23949      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23950      * for the request to the remote server.
23951      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23952      * object into a block of Roo.data.Records.
23953      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23954      * The function must be passed <ul>
23955      * <li>The Record block object</li>
23956      * <li>The "arg" argument from the load function</li>
23957      * <li>A boolean success indicator</li>
23958      * </ul>
23959      * @param {Object} scope The scope in which to call the callback
23960      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23961      */
23962     load : function(params, reader, callback, scope, arg){
23963         if(this.fireEvent("beforeload", this, params) !== false){
23964
23965             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
23966
23967             var url = this.url;
23968             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
23969             if(this.nocache){
23970                 url += "&_dc=" + (new Date().getTime());
23971             }
23972             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
23973             var trans = {
23974                 id : transId,
23975                 cb : "stcCallback"+transId,
23976                 scriptId : "stcScript"+transId,
23977                 params : params,
23978                 arg : arg,
23979                 url : url,
23980                 callback : callback,
23981                 scope : scope,
23982                 reader : reader
23983             };
23984             var conn = this;
23985
23986             window[trans.cb] = function(o){
23987                 conn.handleResponse(o, trans);
23988             };
23989
23990             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
23991
23992             if(this.autoAbort !== false){
23993                 this.abort();
23994             }
23995
23996             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
23997
23998             var script = document.createElement("script");
23999             script.setAttribute("src", url);
24000             script.setAttribute("type", "text/javascript");
24001             script.setAttribute("id", trans.scriptId);
24002             this.head.appendChild(script);
24003
24004             this.trans = trans;
24005         }else{
24006             callback.call(scope||this, null, arg, false);
24007         }
24008     },
24009
24010     // private
24011     isLoading : function(){
24012         return this.trans ? true : false;
24013     },
24014
24015     /**
24016      * Abort the current server request.
24017      */
24018     abort : function(){
24019         if(this.isLoading()){
24020             this.destroyTrans(this.trans);
24021         }
24022     },
24023
24024     // private
24025     destroyTrans : function(trans, isLoaded){
24026         this.head.removeChild(document.getElementById(trans.scriptId));
24027         clearTimeout(trans.timeoutId);
24028         if(isLoaded){
24029             window[trans.cb] = undefined;
24030             try{
24031                 delete window[trans.cb];
24032             }catch(e){}
24033         }else{
24034             // if hasn't been loaded, wait for load to remove it to prevent script error
24035             window[trans.cb] = function(){
24036                 window[trans.cb] = undefined;
24037                 try{
24038                     delete window[trans.cb];
24039                 }catch(e){}
24040             };
24041         }
24042     },
24043
24044     // private
24045     handleResponse : function(o, trans){
24046         this.trans = false;
24047         this.destroyTrans(trans, true);
24048         var result;
24049         try {
24050             result = trans.reader.readRecords(o);
24051         }catch(e){
24052             this.fireEvent("loadexception", this, o, trans.arg, e);
24053             trans.callback.call(trans.scope||window, null, trans.arg, false);
24054             return;
24055         }
24056         this.fireEvent("load", this, o, trans.arg);
24057         trans.callback.call(trans.scope||window, result, trans.arg, true);
24058     },
24059
24060     // private
24061     handleFailure : function(trans){
24062         this.trans = false;
24063         this.destroyTrans(trans, false);
24064         this.fireEvent("loadexception", this, null, trans.arg);
24065         trans.callback.call(trans.scope||window, null, trans.arg, false);
24066     }
24067 });/*
24068  * Based on:
24069  * Ext JS Library 1.1.1
24070  * Copyright(c) 2006-2007, Ext JS, LLC.
24071  *
24072  * Originally Released Under LGPL - original licence link has changed is not relivant.
24073  *
24074  * Fork - LGPL
24075  * <script type="text/javascript">
24076  */
24077
24078 /**
24079  * @class Roo.data.JsonReader
24080  * @extends Roo.data.DataReader
24081  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24082  * based on mappings in a provided Roo.data.Record constructor.
24083  * 
24084  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24085  * in the reply previously. 
24086  * 
24087  * <p>
24088  * Example code:
24089  * <pre><code>
24090 var RecordDef = Roo.data.Record.create([
24091     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24092     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24093 ]);
24094 var myReader = new Roo.data.JsonReader({
24095     totalProperty: "results",    // The property which contains the total dataset size (optional)
24096     root: "rows",                // The property which contains an Array of row objects
24097     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24098 }, RecordDef);
24099 </code></pre>
24100  * <p>
24101  * This would consume a JSON file like this:
24102  * <pre><code>
24103 { 'results': 2, 'rows': [
24104     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24105     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24106 }
24107 </code></pre>
24108  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24109  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24110  * paged from the remote server.
24111  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24112  * @cfg {String} root name of the property which contains the Array of row objects.
24113  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24114  * @cfg {Array} fields Array of field definition objects
24115  * @constructor
24116  * Create a new JsonReader
24117  * @param {Object} meta Metadata configuration options
24118  * @param {Object} recordType Either an Array of field definition objects,
24119  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24120  */
24121 Roo.data.JsonReader = function(meta, recordType){
24122     
24123     meta = meta || {};
24124     // set some defaults:
24125     Roo.applyIf(meta, {
24126         totalProperty: 'total',
24127         successProperty : 'success',
24128         root : 'data',
24129         id : 'id'
24130     });
24131     
24132     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24133 };
24134 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24135     
24136     /**
24137      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24138      * Used by Store query builder to append _requestMeta to params.
24139      * 
24140      */
24141     metaFromRemote : false,
24142     /**
24143      * This method is only used by a DataProxy which has retrieved data from a remote server.
24144      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24145      * @return {Object} data A data block which is used by an Roo.data.Store object as
24146      * a cache of Roo.data.Records.
24147      */
24148     read : function(response){
24149         var json = response.responseText;
24150        
24151         var o = /* eval:var:o */ eval("("+json+")");
24152         if(!o) {
24153             throw {message: "JsonReader.read: Json object not found"};
24154         }
24155         
24156         if(o.metaData){
24157             
24158             delete this.ef;
24159             this.metaFromRemote = true;
24160             this.meta = o.metaData;
24161             this.recordType = Roo.data.Record.create(o.metaData.fields);
24162             this.onMetaChange(this.meta, this.recordType, o);
24163         }
24164         return this.readRecords(o);
24165     },
24166
24167     // private function a store will implement
24168     onMetaChange : function(meta, recordType, o){
24169
24170     },
24171
24172     /**
24173          * @ignore
24174          */
24175     simpleAccess: function(obj, subsc) {
24176         return obj[subsc];
24177     },
24178
24179         /**
24180          * @ignore
24181          */
24182     getJsonAccessor: function(){
24183         var re = /[\[\.]/;
24184         return function(expr) {
24185             try {
24186                 return(re.test(expr))
24187                     ? new Function("obj", "return obj." + expr)
24188                     : function(obj){
24189                         return obj[expr];
24190                     };
24191             } catch(e){}
24192             return Roo.emptyFn;
24193         };
24194     }(),
24195
24196     /**
24197      * Create a data block containing Roo.data.Records from an XML document.
24198      * @param {Object} o An object which contains an Array of row objects in the property specified
24199      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24200      * which contains the total size of the dataset.
24201      * @return {Object} data A data block which is used by an Roo.data.Store object as
24202      * a cache of Roo.data.Records.
24203      */
24204     readRecords : function(o){
24205         /**
24206          * After any data loads, the raw JSON data is available for further custom processing.
24207          * @type Object
24208          */
24209         this.o = o;
24210         var s = this.meta, Record = this.recordType,
24211             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24212
24213 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24214         if (!this.ef) {
24215             if(s.totalProperty) {
24216                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24217                 }
24218                 if(s.successProperty) {
24219                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24220                 }
24221                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24222                 if (s.id) {
24223                         var g = this.getJsonAccessor(s.id);
24224                         this.getId = function(rec) {
24225                                 var r = g(rec);  
24226                                 return (r === undefined || r === "") ? null : r;
24227                         };
24228                 } else {
24229                         this.getId = function(){return null;};
24230                 }
24231             this.ef = [];
24232             for(var jj = 0; jj < fl; jj++){
24233                 f = fi[jj];
24234                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24235                 this.ef[jj] = this.getJsonAccessor(map);
24236             }
24237         }
24238
24239         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24240         if(s.totalProperty){
24241             var vt = parseInt(this.getTotal(o), 10);
24242             if(!isNaN(vt)){
24243                 totalRecords = vt;
24244             }
24245         }
24246         if(s.successProperty){
24247             var vs = this.getSuccess(o);
24248             if(vs === false || vs === 'false'){
24249                 success = false;
24250             }
24251         }
24252         var records = [];
24253         for(var i = 0; i < c; i++){
24254                 var n = root[i];
24255             var values = {};
24256             var id = this.getId(n);
24257             for(var j = 0; j < fl; j++){
24258                 f = fi[j];
24259             var v = this.ef[j](n);
24260             if (!f.convert) {
24261                 Roo.log('missing convert for ' + f.name);
24262                 Roo.log(f);
24263                 continue;
24264             }
24265             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24266             }
24267             var record = new Record(values, id);
24268             record.json = n;
24269             records[i] = record;
24270         }
24271         return {
24272             raw : o,
24273             success : success,
24274             records : records,
24275             totalRecords : totalRecords
24276         };
24277     }
24278 });/*
24279  * Based on:
24280  * Ext JS Library 1.1.1
24281  * Copyright(c) 2006-2007, Ext JS, LLC.
24282  *
24283  * Originally Released Under LGPL - original licence link has changed is not relivant.
24284  *
24285  * Fork - LGPL
24286  * <script type="text/javascript">
24287  */
24288
24289 /**
24290  * @class Roo.data.XmlReader
24291  * @extends Roo.data.DataReader
24292  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24293  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24294  * <p>
24295  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24296  * header in the HTTP response must be set to "text/xml".</em>
24297  * <p>
24298  * Example code:
24299  * <pre><code>
24300 var RecordDef = Roo.data.Record.create([
24301    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24302    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24303 ]);
24304 var myReader = new Roo.data.XmlReader({
24305    totalRecords: "results", // The element which contains the total dataset size (optional)
24306    record: "row",           // The repeated element which contains row information
24307    id: "id"                 // The element within the row that provides an ID for the record (optional)
24308 }, RecordDef);
24309 </code></pre>
24310  * <p>
24311  * This would consume an XML file like this:
24312  * <pre><code>
24313 &lt;?xml?>
24314 &lt;dataset>
24315  &lt;results>2&lt;/results>
24316  &lt;row>
24317    &lt;id>1&lt;/id>
24318    &lt;name>Bill&lt;/name>
24319    &lt;occupation>Gardener&lt;/occupation>
24320  &lt;/row>
24321  &lt;row>
24322    &lt;id>2&lt;/id>
24323    &lt;name>Ben&lt;/name>
24324    &lt;occupation>Horticulturalist&lt;/occupation>
24325  &lt;/row>
24326 &lt;/dataset>
24327 </code></pre>
24328  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24329  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24330  * paged from the remote server.
24331  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24332  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24333  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24334  * a record identifier value.
24335  * @constructor
24336  * Create a new XmlReader
24337  * @param {Object} meta Metadata configuration options
24338  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24339  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24340  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24341  */
24342 Roo.data.XmlReader = function(meta, recordType){
24343     meta = meta || {};
24344     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24345 };
24346 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24347     /**
24348      * This method is only used by a DataProxy which has retrieved data from a remote server.
24349          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24350          * to contain a method called 'responseXML' that returns an XML document object.
24351      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24352      * a cache of Roo.data.Records.
24353      */
24354     read : function(response){
24355         var doc = response.responseXML;
24356         if(!doc) {
24357             throw {message: "XmlReader.read: XML Document not available"};
24358         }
24359         return this.readRecords(doc);
24360     },
24361
24362     /**
24363      * Create a data block containing Roo.data.Records from an XML document.
24364          * @param {Object} doc A parsed XML document.
24365      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24366      * a cache of Roo.data.Records.
24367      */
24368     readRecords : function(doc){
24369         /**
24370          * After any data loads/reads, the raw XML Document is available for further custom processing.
24371          * @type XMLDocument
24372          */
24373         this.xmlData = doc;
24374         var root = doc.documentElement || doc;
24375         var q = Roo.DomQuery;
24376         var recordType = this.recordType, fields = recordType.prototype.fields;
24377         var sid = this.meta.id;
24378         var totalRecords = 0, success = true;
24379         if(this.meta.totalRecords){
24380             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24381         }
24382         
24383         if(this.meta.success){
24384             var sv = q.selectValue(this.meta.success, root, true);
24385             success = sv !== false && sv !== 'false';
24386         }
24387         var records = [];
24388         var ns = q.select(this.meta.record, root);
24389         for(var i = 0, len = ns.length; i < len; i++) {
24390                 var n = ns[i];
24391                 var values = {};
24392                 var id = sid ? q.selectValue(sid, n) : undefined;
24393                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24394                     var f = fields.items[j];
24395                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24396                     v = f.convert(v);
24397                     values[f.name] = v;
24398                 }
24399                 var record = new recordType(values, id);
24400                 record.node = n;
24401                 records[records.length] = record;
24402             }
24403
24404             return {
24405                 success : success,
24406                 records : records,
24407                 totalRecords : totalRecords || records.length
24408             };
24409     }
24410 });/*
24411  * Based on:
24412  * Ext JS Library 1.1.1
24413  * Copyright(c) 2006-2007, Ext JS, LLC.
24414  *
24415  * Originally Released Under LGPL - original licence link has changed is not relivant.
24416  *
24417  * Fork - LGPL
24418  * <script type="text/javascript">
24419  */
24420
24421 /**
24422  * @class Roo.data.ArrayReader
24423  * @extends Roo.data.DataReader
24424  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24425  * Each element of that Array represents a row of data fields. The
24426  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24427  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24428  * <p>
24429  * Example code:.
24430  * <pre><code>
24431 var RecordDef = Roo.data.Record.create([
24432     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24433     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24434 ]);
24435 var myReader = new Roo.data.ArrayReader({
24436     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24437 }, RecordDef);
24438 </code></pre>
24439  * <p>
24440  * This would consume an Array like this:
24441  * <pre><code>
24442 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24443   </code></pre>
24444  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24445  * @constructor
24446  * Create a new JsonReader
24447  * @param {Object} meta Metadata configuration options.
24448  * @param {Object} recordType Either an Array of field definition objects
24449  * as specified to {@link Roo.data.Record#create},
24450  * or an {@link Roo.data.Record} object
24451  * created using {@link Roo.data.Record#create}.
24452  */
24453 Roo.data.ArrayReader = function(meta, recordType){
24454     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24455 };
24456
24457 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24458     /**
24459      * Create a data block containing Roo.data.Records from an XML document.
24460      * @param {Object} o An Array of row objects which represents the dataset.
24461      * @return {Object} data A data block which is used by an Roo.data.Store object as
24462      * a cache of Roo.data.Records.
24463      */
24464     readRecords : function(o){
24465         var sid = this.meta ? this.meta.id : null;
24466         var recordType = this.recordType, fields = recordType.prototype.fields;
24467         var records = [];
24468         var root = o;
24469             for(var i = 0; i < root.length; i++){
24470                     var n = root[i];
24471                 var values = {};
24472                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24473                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24474                 var f = fields.items[j];
24475                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24476                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24477                 v = f.convert(v);
24478                 values[f.name] = v;
24479             }
24480                 var record = new recordType(values, id);
24481                 record.json = n;
24482                 records[records.length] = record;
24483             }
24484             return {
24485                 records : records,
24486                 totalRecords : records.length
24487             };
24488     }
24489 });/*
24490  * Based on:
24491  * Ext JS Library 1.1.1
24492  * Copyright(c) 2006-2007, Ext JS, LLC.
24493  *
24494  * Originally Released Under LGPL - original licence link has changed is not relivant.
24495  *
24496  * Fork - LGPL
24497  * <script type="text/javascript">
24498  */
24499
24500
24501 /**
24502  * @class Roo.data.Tree
24503  * @extends Roo.util.Observable
24504  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24505  * in the tree have most standard DOM functionality.
24506  * @constructor
24507  * @param {Node} root (optional) The root node
24508  */
24509 Roo.data.Tree = function(root){
24510    this.nodeHash = {};
24511    /**
24512     * The root node for this tree
24513     * @type Node
24514     */
24515    this.root = null;
24516    if(root){
24517        this.setRootNode(root);
24518    }
24519    this.addEvents({
24520        /**
24521         * @event append
24522         * Fires when a new child node is appended to a node in this tree.
24523         * @param {Tree} tree The owner tree
24524         * @param {Node} parent The parent node
24525         * @param {Node} node The newly appended node
24526         * @param {Number} index The index of the newly appended node
24527         */
24528        "append" : true,
24529        /**
24530         * @event remove
24531         * Fires when a child node is removed from a node in this tree.
24532         * @param {Tree} tree The owner tree
24533         * @param {Node} parent The parent node
24534         * @param {Node} node The child node removed
24535         */
24536        "remove" : true,
24537        /**
24538         * @event move
24539         * Fires when a node is moved to a new location in the tree
24540         * @param {Tree} tree The owner tree
24541         * @param {Node} node The node moved
24542         * @param {Node} oldParent The old parent of this node
24543         * @param {Node} newParent The new parent of this node
24544         * @param {Number} index The index it was moved to
24545         */
24546        "move" : true,
24547        /**
24548         * @event insert
24549         * Fires when a new child node is inserted in a node in this tree.
24550         * @param {Tree} tree The owner tree
24551         * @param {Node} parent The parent node
24552         * @param {Node} node The child node inserted
24553         * @param {Node} refNode The child node the node was inserted before
24554         */
24555        "insert" : true,
24556        /**
24557         * @event beforeappend
24558         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24559         * @param {Tree} tree The owner tree
24560         * @param {Node} parent The parent node
24561         * @param {Node} node The child node to be appended
24562         */
24563        "beforeappend" : true,
24564        /**
24565         * @event beforeremove
24566         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24567         * @param {Tree} tree The owner tree
24568         * @param {Node} parent The parent node
24569         * @param {Node} node The child node to be removed
24570         */
24571        "beforeremove" : true,
24572        /**
24573         * @event beforemove
24574         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24575         * @param {Tree} tree The owner tree
24576         * @param {Node} node The node being moved
24577         * @param {Node} oldParent The parent of the node
24578         * @param {Node} newParent The new parent the node is moving to
24579         * @param {Number} index The index it is being moved to
24580         */
24581        "beforemove" : true,
24582        /**
24583         * @event beforeinsert
24584         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24585         * @param {Tree} tree The owner tree
24586         * @param {Node} parent The parent node
24587         * @param {Node} node The child node to be inserted
24588         * @param {Node} refNode The child node the node is being inserted before
24589         */
24590        "beforeinsert" : true
24591    });
24592
24593     Roo.data.Tree.superclass.constructor.call(this);
24594 };
24595
24596 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24597     pathSeparator: "/",
24598
24599     proxyNodeEvent : function(){
24600         return this.fireEvent.apply(this, arguments);
24601     },
24602
24603     /**
24604      * Returns the root node for this tree.
24605      * @return {Node}
24606      */
24607     getRootNode : function(){
24608         return this.root;
24609     },
24610
24611     /**
24612      * Sets the root node for this tree.
24613      * @param {Node} node
24614      * @return {Node}
24615      */
24616     setRootNode : function(node){
24617         this.root = node;
24618         node.ownerTree = this;
24619         node.isRoot = true;
24620         this.registerNode(node);
24621         return node;
24622     },
24623
24624     /**
24625      * Gets a node in this tree by its id.
24626      * @param {String} id
24627      * @return {Node}
24628      */
24629     getNodeById : function(id){
24630         return this.nodeHash[id];
24631     },
24632
24633     registerNode : function(node){
24634         this.nodeHash[node.id] = node;
24635     },
24636
24637     unregisterNode : function(node){
24638         delete this.nodeHash[node.id];
24639     },
24640
24641     toString : function(){
24642         return "[Tree"+(this.id?" "+this.id:"")+"]";
24643     }
24644 });
24645
24646 /**
24647  * @class Roo.data.Node
24648  * @extends Roo.util.Observable
24649  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24650  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24651  * @constructor
24652  * @param {Object} attributes The attributes/config for the node
24653  */
24654 Roo.data.Node = function(attributes){
24655     /**
24656      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24657      * @type {Object}
24658      */
24659     this.attributes = attributes || {};
24660     this.leaf = this.attributes.leaf;
24661     /**
24662      * The node id. @type String
24663      */
24664     this.id = this.attributes.id;
24665     if(!this.id){
24666         this.id = Roo.id(null, "ynode-");
24667         this.attributes.id = this.id;
24668     }
24669      
24670     
24671     /**
24672      * All child nodes of this node. @type Array
24673      */
24674     this.childNodes = [];
24675     if(!this.childNodes.indexOf){ // indexOf is a must
24676         this.childNodes.indexOf = function(o){
24677             for(var i = 0, len = this.length; i < len; i++){
24678                 if(this[i] == o) {
24679                     return i;
24680                 }
24681             }
24682             return -1;
24683         };
24684     }
24685     /**
24686      * The parent node for this node. @type Node
24687      */
24688     this.parentNode = null;
24689     /**
24690      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24691      */
24692     this.firstChild = null;
24693     /**
24694      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24695      */
24696     this.lastChild = null;
24697     /**
24698      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24699      */
24700     this.previousSibling = null;
24701     /**
24702      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24703      */
24704     this.nextSibling = null;
24705
24706     this.addEvents({
24707        /**
24708         * @event append
24709         * Fires when a new child node is appended
24710         * @param {Tree} tree The owner tree
24711         * @param {Node} this This node
24712         * @param {Node} node The newly appended node
24713         * @param {Number} index The index of the newly appended node
24714         */
24715        "append" : true,
24716        /**
24717         * @event remove
24718         * Fires when a child node is removed
24719         * @param {Tree} tree The owner tree
24720         * @param {Node} this This node
24721         * @param {Node} node The removed node
24722         */
24723        "remove" : true,
24724        /**
24725         * @event move
24726         * Fires when this node is moved to a new location in the tree
24727         * @param {Tree} tree The owner tree
24728         * @param {Node} this This node
24729         * @param {Node} oldParent The old parent of this node
24730         * @param {Node} newParent The new parent of this node
24731         * @param {Number} index The index it was moved to
24732         */
24733        "move" : true,
24734        /**
24735         * @event insert
24736         * Fires when a new child node is inserted.
24737         * @param {Tree} tree The owner tree
24738         * @param {Node} this This node
24739         * @param {Node} node The child node inserted
24740         * @param {Node} refNode The child node the node was inserted before
24741         */
24742        "insert" : true,
24743        /**
24744         * @event beforeappend
24745         * Fires before a new child is appended, return false to cancel the append.
24746         * @param {Tree} tree The owner tree
24747         * @param {Node} this This node
24748         * @param {Node} node The child node to be appended
24749         */
24750        "beforeappend" : true,
24751        /**
24752         * @event beforeremove
24753         * Fires before a child is removed, return false to cancel the remove.
24754         * @param {Tree} tree The owner tree
24755         * @param {Node} this This node
24756         * @param {Node} node The child node to be removed
24757         */
24758        "beforeremove" : true,
24759        /**
24760         * @event beforemove
24761         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24762         * @param {Tree} tree The owner tree
24763         * @param {Node} this This node
24764         * @param {Node} oldParent The parent of this node
24765         * @param {Node} newParent The new parent this node is moving to
24766         * @param {Number} index The index it is being moved to
24767         */
24768        "beforemove" : true,
24769        /**
24770         * @event beforeinsert
24771         * Fires before a new child is inserted, return false to cancel the insert.
24772         * @param {Tree} tree The owner tree
24773         * @param {Node} this This node
24774         * @param {Node} node The child node to be inserted
24775         * @param {Node} refNode The child node the node is being inserted before
24776         */
24777        "beforeinsert" : true
24778    });
24779     this.listeners = this.attributes.listeners;
24780     Roo.data.Node.superclass.constructor.call(this);
24781 };
24782
24783 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24784     fireEvent : function(evtName){
24785         // first do standard event for this node
24786         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24787             return false;
24788         }
24789         // then bubble it up to the tree if the event wasn't cancelled
24790         var ot = this.getOwnerTree();
24791         if(ot){
24792             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24793                 return false;
24794             }
24795         }
24796         return true;
24797     },
24798
24799     /**
24800      * Returns true if this node is a leaf
24801      * @return {Boolean}
24802      */
24803     isLeaf : function(){
24804         return this.leaf === true;
24805     },
24806
24807     // private
24808     setFirstChild : function(node){
24809         this.firstChild = node;
24810     },
24811
24812     //private
24813     setLastChild : function(node){
24814         this.lastChild = node;
24815     },
24816
24817
24818     /**
24819      * Returns true if this node is the last child of its parent
24820      * @return {Boolean}
24821      */
24822     isLast : function(){
24823        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24824     },
24825
24826     /**
24827      * Returns true if this node is the first child of its parent
24828      * @return {Boolean}
24829      */
24830     isFirst : function(){
24831        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24832     },
24833
24834     hasChildNodes : function(){
24835         return !this.isLeaf() && this.childNodes.length > 0;
24836     },
24837
24838     /**
24839      * Insert node(s) as the last child node of this node.
24840      * @param {Node/Array} node The node or Array of nodes to append
24841      * @return {Node} The appended node if single append, or null if an array was passed
24842      */
24843     appendChild : function(node){
24844         var multi = false;
24845         if(node instanceof Array){
24846             multi = node;
24847         }else if(arguments.length > 1){
24848             multi = arguments;
24849         }
24850         // if passed an array or multiple args do them one by one
24851         if(multi){
24852             for(var i = 0, len = multi.length; i < len; i++) {
24853                 this.appendChild(multi[i]);
24854             }
24855         }else{
24856             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24857                 return false;
24858             }
24859             var index = this.childNodes.length;
24860             var oldParent = node.parentNode;
24861             // it's a move, make sure we move it cleanly
24862             if(oldParent){
24863                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24864                     return false;
24865                 }
24866                 oldParent.removeChild(node);
24867             }
24868             index = this.childNodes.length;
24869             if(index == 0){
24870                 this.setFirstChild(node);
24871             }
24872             this.childNodes.push(node);
24873             node.parentNode = this;
24874             var ps = this.childNodes[index-1];
24875             if(ps){
24876                 node.previousSibling = ps;
24877                 ps.nextSibling = node;
24878             }else{
24879                 node.previousSibling = null;
24880             }
24881             node.nextSibling = null;
24882             this.setLastChild(node);
24883             node.setOwnerTree(this.getOwnerTree());
24884             this.fireEvent("append", this.ownerTree, this, node, index);
24885             if(oldParent){
24886                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24887             }
24888             return node;
24889         }
24890     },
24891
24892     /**
24893      * Removes a child node from this node.
24894      * @param {Node} node The node to remove
24895      * @return {Node} The removed node
24896      */
24897     removeChild : function(node){
24898         var index = this.childNodes.indexOf(node);
24899         if(index == -1){
24900             return false;
24901         }
24902         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24903             return false;
24904         }
24905
24906         // remove it from childNodes collection
24907         this.childNodes.splice(index, 1);
24908
24909         // update siblings
24910         if(node.previousSibling){
24911             node.previousSibling.nextSibling = node.nextSibling;
24912         }
24913         if(node.nextSibling){
24914             node.nextSibling.previousSibling = node.previousSibling;
24915         }
24916
24917         // update child refs
24918         if(this.firstChild == node){
24919             this.setFirstChild(node.nextSibling);
24920         }
24921         if(this.lastChild == node){
24922             this.setLastChild(node.previousSibling);
24923         }
24924
24925         node.setOwnerTree(null);
24926         // clear any references from the node
24927         node.parentNode = null;
24928         node.previousSibling = null;
24929         node.nextSibling = null;
24930         this.fireEvent("remove", this.ownerTree, this, node);
24931         return node;
24932     },
24933
24934     /**
24935      * Inserts the first node before the second node in this nodes childNodes collection.
24936      * @param {Node} node The node to insert
24937      * @param {Node} refNode The node to insert before (if null the node is appended)
24938      * @return {Node} The inserted node
24939      */
24940     insertBefore : function(node, refNode){
24941         if(!refNode){ // like standard Dom, refNode can be null for append
24942             return this.appendChild(node);
24943         }
24944         // nothing to do
24945         if(node == refNode){
24946             return false;
24947         }
24948
24949         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24950             return false;
24951         }
24952         var index = this.childNodes.indexOf(refNode);
24953         var oldParent = node.parentNode;
24954         var refIndex = index;
24955
24956         // when moving internally, indexes will change after remove
24957         if(oldParent == this && this.childNodes.indexOf(node) < index){
24958             refIndex--;
24959         }
24960
24961         // it's a move, make sure we move it cleanly
24962         if(oldParent){
24963             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24964                 return false;
24965             }
24966             oldParent.removeChild(node);
24967         }
24968         if(refIndex == 0){
24969             this.setFirstChild(node);
24970         }
24971         this.childNodes.splice(refIndex, 0, node);
24972         node.parentNode = this;
24973         var ps = this.childNodes[refIndex-1];
24974         if(ps){
24975             node.previousSibling = ps;
24976             ps.nextSibling = node;
24977         }else{
24978             node.previousSibling = null;
24979         }
24980         node.nextSibling = refNode;
24981         refNode.previousSibling = node;
24982         node.setOwnerTree(this.getOwnerTree());
24983         this.fireEvent("insert", this.ownerTree, this, node, refNode);
24984         if(oldParent){
24985             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
24986         }
24987         return node;
24988     },
24989
24990     /**
24991      * Returns the child node at the specified index.
24992      * @param {Number} index
24993      * @return {Node}
24994      */
24995     item : function(index){
24996         return this.childNodes[index];
24997     },
24998
24999     /**
25000      * Replaces one child node in this node with another.
25001      * @param {Node} newChild The replacement node
25002      * @param {Node} oldChild The node to replace
25003      * @return {Node} The replaced node
25004      */
25005     replaceChild : function(newChild, oldChild){
25006         this.insertBefore(newChild, oldChild);
25007         this.removeChild(oldChild);
25008         return oldChild;
25009     },
25010
25011     /**
25012      * Returns the index of a child node
25013      * @param {Node} node
25014      * @return {Number} The index of the node or -1 if it was not found
25015      */
25016     indexOf : function(child){
25017         return this.childNodes.indexOf(child);
25018     },
25019
25020     /**
25021      * Returns the tree this node is in.
25022      * @return {Tree}
25023      */
25024     getOwnerTree : function(){
25025         // if it doesn't have one, look for one
25026         if(!this.ownerTree){
25027             var p = this;
25028             while(p){
25029                 if(p.ownerTree){
25030                     this.ownerTree = p.ownerTree;
25031                     break;
25032                 }
25033                 p = p.parentNode;
25034             }
25035         }
25036         return this.ownerTree;
25037     },
25038
25039     /**
25040      * Returns depth of this node (the root node has a depth of 0)
25041      * @return {Number}
25042      */
25043     getDepth : function(){
25044         var depth = 0;
25045         var p = this;
25046         while(p.parentNode){
25047             ++depth;
25048             p = p.parentNode;
25049         }
25050         return depth;
25051     },
25052
25053     // private
25054     setOwnerTree : function(tree){
25055         // if it's move, we need to update everyone
25056         if(tree != this.ownerTree){
25057             if(this.ownerTree){
25058                 this.ownerTree.unregisterNode(this);
25059             }
25060             this.ownerTree = tree;
25061             var cs = this.childNodes;
25062             for(var i = 0, len = cs.length; i < len; i++) {
25063                 cs[i].setOwnerTree(tree);
25064             }
25065             if(tree){
25066                 tree.registerNode(this);
25067             }
25068         }
25069     },
25070
25071     /**
25072      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25073      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25074      * @return {String} The path
25075      */
25076     getPath : function(attr){
25077         attr = attr || "id";
25078         var p = this.parentNode;
25079         var b = [this.attributes[attr]];
25080         while(p){
25081             b.unshift(p.attributes[attr]);
25082             p = p.parentNode;
25083         }
25084         var sep = this.getOwnerTree().pathSeparator;
25085         return sep + b.join(sep);
25086     },
25087
25088     /**
25089      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25090      * function call will be the scope provided or the current node. The arguments to the function
25091      * will be the args provided or the current node. If the function returns false at any point,
25092      * the bubble is stopped.
25093      * @param {Function} fn The function to call
25094      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25095      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25096      */
25097     bubble : function(fn, scope, args){
25098         var p = this;
25099         while(p){
25100             if(fn.call(scope || p, args || p) === false){
25101                 break;
25102             }
25103             p = p.parentNode;
25104         }
25105     },
25106
25107     /**
25108      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25109      * function call will be the scope provided or the current node. The arguments to the function
25110      * will be the args provided or the current node. If the function returns false at any point,
25111      * the cascade is stopped on that branch.
25112      * @param {Function} fn The function to call
25113      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25114      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25115      */
25116     cascade : function(fn, scope, args){
25117         if(fn.call(scope || this, args || this) !== false){
25118             var cs = this.childNodes;
25119             for(var i = 0, len = cs.length; i < len; i++) {
25120                 cs[i].cascade(fn, scope, args);
25121             }
25122         }
25123     },
25124
25125     /**
25126      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25127      * function call will be the scope provided or the current node. The arguments to the function
25128      * will be the args provided or the current node. If the function returns false at any point,
25129      * the iteration stops.
25130      * @param {Function} fn The function to call
25131      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25132      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25133      */
25134     eachChild : function(fn, scope, args){
25135         var cs = this.childNodes;
25136         for(var i = 0, len = cs.length; i < len; i++) {
25137                 if(fn.call(scope || this, args || cs[i]) === false){
25138                     break;
25139                 }
25140         }
25141     },
25142
25143     /**
25144      * Finds the first child that has the attribute with the specified value.
25145      * @param {String} attribute The attribute name
25146      * @param {Mixed} value The value to search for
25147      * @return {Node} The found child or null if none was found
25148      */
25149     findChild : function(attribute, value){
25150         var cs = this.childNodes;
25151         for(var i = 0, len = cs.length; i < len; i++) {
25152                 if(cs[i].attributes[attribute] == value){
25153                     return cs[i];
25154                 }
25155         }
25156         return null;
25157     },
25158
25159     /**
25160      * Finds the first child by a custom function. The child matches if the function passed
25161      * returns true.
25162      * @param {Function} fn
25163      * @param {Object} scope (optional)
25164      * @return {Node} The found child or null if none was found
25165      */
25166     findChildBy : function(fn, scope){
25167         var cs = this.childNodes;
25168         for(var i = 0, len = cs.length; i < len; i++) {
25169                 if(fn.call(scope||cs[i], cs[i]) === true){
25170                     return cs[i];
25171                 }
25172         }
25173         return null;
25174     },
25175
25176     /**
25177      * Sorts this nodes children using the supplied sort function
25178      * @param {Function} fn
25179      * @param {Object} scope (optional)
25180      */
25181     sort : function(fn, scope){
25182         var cs = this.childNodes;
25183         var len = cs.length;
25184         if(len > 0){
25185             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25186             cs.sort(sortFn);
25187             for(var i = 0; i < len; i++){
25188                 var n = cs[i];
25189                 n.previousSibling = cs[i-1];
25190                 n.nextSibling = cs[i+1];
25191                 if(i == 0){
25192                     this.setFirstChild(n);
25193                 }
25194                 if(i == len-1){
25195                     this.setLastChild(n);
25196                 }
25197             }
25198         }
25199     },
25200
25201     /**
25202      * Returns true if this node is an ancestor (at any point) of the passed node.
25203      * @param {Node} node
25204      * @return {Boolean}
25205      */
25206     contains : function(node){
25207         return node.isAncestor(this);
25208     },
25209
25210     /**
25211      * Returns true if the passed node is an ancestor (at any point) of this node.
25212      * @param {Node} node
25213      * @return {Boolean}
25214      */
25215     isAncestor : function(node){
25216         var p = this.parentNode;
25217         while(p){
25218             if(p == node){
25219                 return true;
25220             }
25221             p = p.parentNode;
25222         }
25223         return false;
25224     },
25225
25226     toString : function(){
25227         return "[Node"+(this.id?" "+this.id:"")+"]";
25228     }
25229 });/*
25230  * Based on:
25231  * Ext JS Library 1.1.1
25232  * Copyright(c) 2006-2007, Ext JS, LLC.
25233  *
25234  * Originally Released Under LGPL - original licence link has changed is not relivant.
25235  *
25236  * Fork - LGPL
25237  * <script type="text/javascript">
25238  */
25239  (function(){ 
25240 /**
25241  * @class Roo.Layer
25242  * @extends Roo.Element
25243  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25244  * automatic maintaining of shadow/shim positions.
25245  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25246  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25247  * you can pass a string with a CSS class name. False turns off the shadow.
25248  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25249  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25250  * @cfg {String} cls CSS class to add to the element
25251  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25252  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25253  * @constructor
25254  * @param {Object} config An object with config options.
25255  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25256  */
25257
25258 Roo.Layer = function(config, existingEl){
25259     config = config || {};
25260     var dh = Roo.DomHelper;
25261     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25262     if(existingEl){
25263         this.dom = Roo.getDom(existingEl);
25264     }
25265     if(!this.dom){
25266         var o = config.dh || {tag: "div", cls: "x-layer"};
25267         this.dom = dh.append(pel, o);
25268     }
25269     if(config.cls){
25270         this.addClass(config.cls);
25271     }
25272     this.constrain = config.constrain !== false;
25273     this.visibilityMode = Roo.Element.VISIBILITY;
25274     if(config.id){
25275         this.id = this.dom.id = config.id;
25276     }else{
25277         this.id = Roo.id(this.dom);
25278     }
25279     this.zindex = config.zindex || this.getZIndex();
25280     this.position("absolute", this.zindex);
25281     if(config.shadow){
25282         this.shadowOffset = config.shadowOffset || 4;
25283         this.shadow = new Roo.Shadow({
25284             offset : this.shadowOffset,
25285             mode : config.shadow
25286         });
25287     }else{
25288         this.shadowOffset = 0;
25289     }
25290     this.useShim = config.shim !== false && Roo.useShims;
25291     this.useDisplay = config.useDisplay;
25292     this.hide();
25293 };
25294
25295 var supr = Roo.Element.prototype;
25296
25297 // shims are shared among layer to keep from having 100 iframes
25298 var shims = [];
25299
25300 Roo.extend(Roo.Layer, Roo.Element, {
25301
25302     getZIndex : function(){
25303         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25304     },
25305
25306     getShim : function(){
25307         if(!this.useShim){
25308             return null;
25309         }
25310         if(this.shim){
25311             return this.shim;
25312         }
25313         var shim = shims.shift();
25314         if(!shim){
25315             shim = this.createShim();
25316             shim.enableDisplayMode('block');
25317             shim.dom.style.display = 'none';
25318             shim.dom.style.visibility = 'visible';
25319         }
25320         var pn = this.dom.parentNode;
25321         if(shim.dom.parentNode != pn){
25322             pn.insertBefore(shim.dom, this.dom);
25323         }
25324         shim.setStyle('z-index', this.getZIndex()-2);
25325         this.shim = shim;
25326         return shim;
25327     },
25328
25329     hideShim : function(){
25330         if(this.shim){
25331             this.shim.setDisplayed(false);
25332             shims.push(this.shim);
25333             delete this.shim;
25334         }
25335     },
25336
25337     disableShadow : function(){
25338         if(this.shadow){
25339             this.shadowDisabled = true;
25340             this.shadow.hide();
25341             this.lastShadowOffset = this.shadowOffset;
25342             this.shadowOffset = 0;
25343         }
25344     },
25345
25346     enableShadow : function(show){
25347         if(this.shadow){
25348             this.shadowDisabled = false;
25349             this.shadowOffset = this.lastShadowOffset;
25350             delete this.lastShadowOffset;
25351             if(show){
25352                 this.sync(true);
25353             }
25354         }
25355     },
25356
25357     // private
25358     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25359     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25360     sync : function(doShow){
25361         var sw = this.shadow;
25362         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25363             var sh = this.getShim();
25364
25365             var w = this.getWidth(),
25366                 h = this.getHeight();
25367
25368             var l = this.getLeft(true),
25369                 t = this.getTop(true);
25370
25371             if(sw && !this.shadowDisabled){
25372                 if(doShow && !sw.isVisible()){
25373                     sw.show(this);
25374                 }else{
25375                     sw.realign(l, t, w, h);
25376                 }
25377                 if(sh){
25378                     if(doShow){
25379                        sh.show();
25380                     }
25381                     // fit the shim behind the shadow, so it is shimmed too
25382                     var a = sw.adjusts, s = sh.dom.style;
25383                     s.left = (Math.min(l, l+a.l))+"px";
25384                     s.top = (Math.min(t, t+a.t))+"px";
25385                     s.width = (w+a.w)+"px";
25386                     s.height = (h+a.h)+"px";
25387                 }
25388             }else if(sh){
25389                 if(doShow){
25390                    sh.show();
25391                 }
25392                 sh.setSize(w, h);
25393                 sh.setLeftTop(l, t);
25394             }
25395             
25396         }
25397     },
25398
25399     // private
25400     destroy : function(){
25401         this.hideShim();
25402         if(this.shadow){
25403             this.shadow.hide();
25404         }
25405         this.removeAllListeners();
25406         var pn = this.dom.parentNode;
25407         if(pn){
25408             pn.removeChild(this.dom);
25409         }
25410         Roo.Element.uncache(this.id);
25411     },
25412
25413     remove : function(){
25414         this.destroy();
25415     },
25416
25417     // private
25418     beginUpdate : function(){
25419         this.updating = true;
25420     },
25421
25422     // private
25423     endUpdate : function(){
25424         this.updating = false;
25425         this.sync(true);
25426     },
25427
25428     // private
25429     hideUnders : function(negOffset){
25430         if(this.shadow){
25431             this.shadow.hide();
25432         }
25433         this.hideShim();
25434     },
25435
25436     // private
25437     constrainXY : function(){
25438         if(this.constrain){
25439             var vw = Roo.lib.Dom.getViewWidth(),
25440                 vh = Roo.lib.Dom.getViewHeight();
25441             var s = Roo.get(document).getScroll();
25442
25443             var xy = this.getXY();
25444             var x = xy[0], y = xy[1];   
25445             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25446             // only move it if it needs it
25447             var moved = false;
25448             // first validate right/bottom
25449             if((x + w) > vw+s.left){
25450                 x = vw - w - this.shadowOffset;
25451                 moved = true;
25452             }
25453             if((y + h) > vh+s.top){
25454                 y = vh - h - this.shadowOffset;
25455                 moved = true;
25456             }
25457             // then make sure top/left isn't negative
25458             if(x < s.left){
25459                 x = s.left;
25460                 moved = true;
25461             }
25462             if(y < s.top){
25463                 y = s.top;
25464                 moved = true;
25465             }
25466             if(moved){
25467                 if(this.avoidY){
25468                     var ay = this.avoidY;
25469                     if(y <= ay && (y+h) >= ay){
25470                         y = ay-h-5;   
25471                     }
25472                 }
25473                 xy = [x, y];
25474                 this.storeXY(xy);
25475                 supr.setXY.call(this, xy);
25476                 this.sync();
25477             }
25478         }
25479     },
25480
25481     isVisible : function(){
25482         return this.visible;    
25483     },
25484
25485     // private
25486     showAction : function(){
25487         this.visible = true; // track visibility to prevent getStyle calls
25488         if(this.useDisplay === true){
25489             this.setDisplayed("");
25490         }else if(this.lastXY){
25491             supr.setXY.call(this, this.lastXY);
25492         }else if(this.lastLT){
25493             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25494         }
25495     },
25496
25497     // private
25498     hideAction : function(){
25499         this.visible = false;
25500         if(this.useDisplay === true){
25501             this.setDisplayed(false);
25502         }else{
25503             this.setLeftTop(-10000,-10000);
25504         }
25505     },
25506
25507     // overridden Element method
25508     setVisible : function(v, a, d, c, e){
25509         if(v){
25510             this.showAction();
25511         }
25512         if(a && v){
25513             var cb = function(){
25514                 this.sync(true);
25515                 if(c){
25516                     c();
25517                 }
25518             }.createDelegate(this);
25519             supr.setVisible.call(this, true, true, d, cb, e);
25520         }else{
25521             if(!v){
25522                 this.hideUnders(true);
25523             }
25524             var cb = c;
25525             if(a){
25526                 cb = function(){
25527                     this.hideAction();
25528                     if(c){
25529                         c();
25530                     }
25531                 }.createDelegate(this);
25532             }
25533             supr.setVisible.call(this, v, a, d, cb, e);
25534             if(v){
25535                 this.sync(true);
25536             }else if(!a){
25537                 this.hideAction();
25538             }
25539         }
25540     },
25541
25542     storeXY : function(xy){
25543         delete this.lastLT;
25544         this.lastXY = xy;
25545     },
25546
25547     storeLeftTop : function(left, top){
25548         delete this.lastXY;
25549         this.lastLT = [left, top];
25550     },
25551
25552     // private
25553     beforeFx : function(){
25554         this.beforeAction();
25555         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25556     },
25557
25558     // private
25559     afterFx : function(){
25560         Roo.Layer.superclass.afterFx.apply(this, arguments);
25561         this.sync(this.isVisible());
25562     },
25563
25564     // private
25565     beforeAction : function(){
25566         if(!this.updating && this.shadow){
25567             this.shadow.hide();
25568         }
25569     },
25570
25571     // overridden Element method
25572     setLeft : function(left){
25573         this.storeLeftTop(left, this.getTop(true));
25574         supr.setLeft.apply(this, arguments);
25575         this.sync();
25576     },
25577
25578     setTop : function(top){
25579         this.storeLeftTop(this.getLeft(true), top);
25580         supr.setTop.apply(this, arguments);
25581         this.sync();
25582     },
25583
25584     setLeftTop : function(left, top){
25585         this.storeLeftTop(left, top);
25586         supr.setLeftTop.apply(this, arguments);
25587         this.sync();
25588     },
25589
25590     setXY : function(xy, a, d, c, e){
25591         this.fixDisplay();
25592         this.beforeAction();
25593         this.storeXY(xy);
25594         var cb = this.createCB(c);
25595         supr.setXY.call(this, xy, a, d, cb, e);
25596         if(!a){
25597             cb();
25598         }
25599     },
25600
25601     // private
25602     createCB : function(c){
25603         var el = this;
25604         return function(){
25605             el.constrainXY();
25606             el.sync(true);
25607             if(c){
25608                 c();
25609             }
25610         };
25611     },
25612
25613     // overridden Element method
25614     setX : function(x, a, d, c, e){
25615         this.setXY([x, this.getY()], a, d, c, e);
25616     },
25617
25618     // overridden Element method
25619     setY : function(y, a, d, c, e){
25620         this.setXY([this.getX(), y], a, d, c, e);
25621     },
25622
25623     // overridden Element method
25624     setSize : function(w, h, a, d, c, e){
25625         this.beforeAction();
25626         var cb = this.createCB(c);
25627         supr.setSize.call(this, w, h, a, d, cb, e);
25628         if(!a){
25629             cb();
25630         }
25631     },
25632
25633     // overridden Element method
25634     setWidth : function(w, a, d, c, e){
25635         this.beforeAction();
25636         var cb = this.createCB(c);
25637         supr.setWidth.call(this, w, a, d, cb, e);
25638         if(!a){
25639             cb();
25640         }
25641     },
25642
25643     // overridden Element method
25644     setHeight : function(h, a, d, c, e){
25645         this.beforeAction();
25646         var cb = this.createCB(c);
25647         supr.setHeight.call(this, h, a, d, cb, e);
25648         if(!a){
25649             cb();
25650         }
25651     },
25652
25653     // overridden Element method
25654     setBounds : function(x, y, w, h, a, d, c, e){
25655         this.beforeAction();
25656         var cb = this.createCB(c);
25657         if(!a){
25658             this.storeXY([x, y]);
25659             supr.setXY.call(this, [x, y]);
25660             supr.setSize.call(this, w, h, a, d, cb, e);
25661             cb();
25662         }else{
25663             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25664         }
25665         return this;
25666     },
25667     
25668     /**
25669      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25670      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25671      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25672      * @param {Number} zindex The new z-index to set
25673      * @return {this} The Layer
25674      */
25675     setZIndex : function(zindex){
25676         this.zindex = zindex;
25677         this.setStyle("z-index", zindex + 2);
25678         if(this.shadow){
25679             this.shadow.setZIndex(zindex + 1);
25680         }
25681         if(this.shim){
25682             this.shim.setStyle("z-index", zindex);
25683         }
25684     }
25685 });
25686 })();/*
25687  * Based on:
25688  * Ext JS Library 1.1.1
25689  * Copyright(c) 2006-2007, Ext JS, LLC.
25690  *
25691  * Originally Released Under LGPL - original licence link has changed is not relivant.
25692  *
25693  * Fork - LGPL
25694  * <script type="text/javascript">
25695  */
25696
25697
25698 /**
25699  * @class Roo.Shadow
25700  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25701  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25702  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25703  * @constructor
25704  * Create a new Shadow
25705  * @param {Object} config The config object
25706  */
25707 Roo.Shadow = function(config){
25708     Roo.apply(this, config);
25709     if(typeof this.mode != "string"){
25710         this.mode = this.defaultMode;
25711     }
25712     var o = this.offset, a = {h: 0};
25713     var rad = Math.floor(this.offset/2);
25714     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25715         case "drop":
25716             a.w = 0;
25717             a.l = a.t = o;
25718             a.t -= 1;
25719             if(Roo.isIE){
25720                 a.l -= this.offset + rad;
25721                 a.t -= this.offset + rad;
25722                 a.w -= rad;
25723                 a.h -= rad;
25724                 a.t += 1;
25725             }
25726         break;
25727         case "sides":
25728             a.w = (o*2);
25729             a.l = -o;
25730             a.t = o-1;
25731             if(Roo.isIE){
25732                 a.l -= (this.offset - rad);
25733                 a.t -= this.offset + rad;
25734                 a.l += 1;
25735                 a.w -= (this.offset - rad)*2;
25736                 a.w -= rad + 1;
25737                 a.h -= 1;
25738             }
25739         break;
25740         case "frame":
25741             a.w = a.h = (o*2);
25742             a.l = a.t = -o;
25743             a.t += 1;
25744             a.h -= 2;
25745             if(Roo.isIE){
25746                 a.l -= (this.offset - rad);
25747                 a.t -= (this.offset - rad);
25748                 a.l += 1;
25749                 a.w -= (this.offset + rad + 1);
25750                 a.h -= (this.offset + rad);
25751                 a.h += 1;
25752             }
25753         break;
25754     };
25755
25756     this.adjusts = a;
25757 };
25758
25759 Roo.Shadow.prototype = {
25760     /**
25761      * @cfg {String} mode
25762      * The shadow display mode.  Supports the following options:<br />
25763      * sides: Shadow displays on both sides and bottom only<br />
25764      * frame: Shadow displays equally on all four sides<br />
25765      * drop: Traditional bottom-right drop shadow (default)
25766      */
25767     /**
25768      * @cfg {String} offset
25769      * The number of pixels to offset the shadow from the element (defaults to 4)
25770      */
25771     offset: 4,
25772
25773     // private
25774     defaultMode: "drop",
25775
25776     /**
25777      * Displays the shadow under the target element
25778      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25779      */
25780     show : function(target){
25781         target = Roo.get(target);
25782         if(!this.el){
25783             this.el = Roo.Shadow.Pool.pull();
25784             if(this.el.dom.nextSibling != target.dom){
25785                 this.el.insertBefore(target);
25786             }
25787         }
25788         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25789         if(Roo.isIE){
25790             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25791         }
25792         this.realign(
25793             target.getLeft(true),
25794             target.getTop(true),
25795             target.getWidth(),
25796             target.getHeight()
25797         );
25798         this.el.dom.style.display = "block";
25799     },
25800
25801     /**
25802      * Returns true if the shadow is visible, else false
25803      */
25804     isVisible : function(){
25805         return this.el ? true : false;  
25806     },
25807
25808     /**
25809      * Direct alignment when values are already available. Show must be called at least once before
25810      * calling this method to ensure it is initialized.
25811      * @param {Number} left The target element left position
25812      * @param {Number} top The target element top position
25813      * @param {Number} width The target element width
25814      * @param {Number} height The target element height
25815      */
25816     realign : function(l, t, w, h){
25817         if(!this.el){
25818             return;
25819         }
25820         var a = this.adjusts, d = this.el.dom, s = d.style;
25821         var iea = 0;
25822         s.left = (l+a.l)+"px";
25823         s.top = (t+a.t)+"px";
25824         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25825  
25826         if(s.width != sws || s.height != shs){
25827             s.width = sws;
25828             s.height = shs;
25829             if(!Roo.isIE){
25830                 var cn = d.childNodes;
25831                 var sww = Math.max(0, (sw-12))+"px";
25832                 cn[0].childNodes[1].style.width = sww;
25833                 cn[1].childNodes[1].style.width = sww;
25834                 cn[2].childNodes[1].style.width = sww;
25835                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25836             }
25837         }
25838     },
25839
25840     /**
25841      * Hides this shadow
25842      */
25843     hide : function(){
25844         if(this.el){
25845             this.el.dom.style.display = "none";
25846             Roo.Shadow.Pool.push(this.el);
25847             delete this.el;
25848         }
25849     },
25850
25851     /**
25852      * Adjust the z-index of this shadow
25853      * @param {Number} zindex The new z-index
25854      */
25855     setZIndex : function(z){
25856         this.zIndex = z;
25857         if(this.el){
25858             this.el.setStyle("z-index", z);
25859         }
25860     }
25861 };
25862
25863 // Private utility class that manages the internal Shadow cache
25864 Roo.Shadow.Pool = function(){
25865     var p = [];
25866     var markup = Roo.isIE ?
25867                  '<div class="x-ie-shadow"></div>' :
25868                  '<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>';
25869     return {
25870         pull : function(){
25871             var sh = p.shift();
25872             if(!sh){
25873                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25874                 sh.autoBoxAdjust = false;
25875             }
25876             return sh;
25877         },
25878
25879         push : function(sh){
25880             p.push(sh);
25881         }
25882     };
25883 }();/*
25884  * Based on:
25885  * Ext JS Library 1.1.1
25886  * Copyright(c) 2006-2007, Ext JS, LLC.
25887  *
25888  * Originally Released Under LGPL - original licence link has changed is not relivant.
25889  *
25890  * Fork - LGPL
25891  * <script type="text/javascript">
25892  */
25893
25894
25895 /**
25896  * @class Roo.SplitBar
25897  * @extends Roo.util.Observable
25898  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25899  * <br><br>
25900  * Usage:
25901  * <pre><code>
25902 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25903                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25904 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25905 split.minSize = 100;
25906 split.maxSize = 600;
25907 split.animate = true;
25908 split.on('moved', splitterMoved);
25909 </code></pre>
25910  * @constructor
25911  * Create a new SplitBar
25912  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25913  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25914  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25915  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25916                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25917                         position of the SplitBar).
25918  */
25919 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25920     
25921     /** @private */
25922     this.el = Roo.get(dragElement, true);
25923     this.el.dom.unselectable = "on";
25924     /** @private */
25925     this.resizingEl = Roo.get(resizingElement, true);
25926
25927     /**
25928      * @private
25929      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25930      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25931      * @type Number
25932      */
25933     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25934     
25935     /**
25936      * The minimum size of the resizing element. (Defaults to 0)
25937      * @type Number
25938      */
25939     this.minSize = 0;
25940     
25941     /**
25942      * The maximum size of the resizing element. (Defaults to 2000)
25943      * @type Number
25944      */
25945     this.maxSize = 2000;
25946     
25947     /**
25948      * Whether to animate the transition to the new size
25949      * @type Boolean
25950      */
25951     this.animate = false;
25952     
25953     /**
25954      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25955      * @type Boolean
25956      */
25957     this.useShim = false;
25958     
25959     /** @private */
25960     this.shim = null;
25961     
25962     if(!existingProxy){
25963         /** @private */
25964         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25965     }else{
25966         this.proxy = Roo.get(existingProxy).dom;
25967     }
25968     /** @private */
25969     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
25970     
25971     /** @private */
25972     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
25973     
25974     /** @private */
25975     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
25976     
25977     /** @private */
25978     this.dragSpecs = {};
25979     
25980     /**
25981      * @private The adapter to use to positon and resize elements
25982      */
25983     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
25984     this.adapter.init(this);
25985     
25986     if(this.orientation == Roo.SplitBar.HORIZONTAL){
25987         /** @private */
25988         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
25989         this.el.addClass("x-splitbar-h");
25990     }else{
25991         /** @private */
25992         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
25993         this.el.addClass("x-splitbar-v");
25994     }
25995     
25996     this.addEvents({
25997         /**
25998          * @event resize
25999          * Fires when the splitter is moved (alias for {@link #event-moved})
26000          * @param {Roo.SplitBar} this
26001          * @param {Number} newSize the new width or height
26002          */
26003         "resize" : true,
26004         /**
26005          * @event moved
26006          * Fires when the splitter is moved
26007          * @param {Roo.SplitBar} this
26008          * @param {Number} newSize the new width or height
26009          */
26010         "moved" : true,
26011         /**
26012          * @event beforeresize
26013          * Fires before the splitter is dragged
26014          * @param {Roo.SplitBar} this
26015          */
26016         "beforeresize" : true,
26017
26018         "beforeapply" : true
26019     });
26020
26021     Roo.util.Observable.call(this);
26022 };
26023
26024 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26025     onStartProxyDrag : function(x, y){
26026         this.fireEvent("beforeresize", this);
26027         if(!this.overlay){
26028             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26029             o.unselectable();
26030             o.enableDisplayMode("block");
26031             // all splitbars share the same overlay
26032             Roo.SplitBar.prototype.overlay = o;
26033         }
26034         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26035         this.overlay.show();
26036         Roo.get(this.proxy).setDisplayed("block");
26037         var size = this.adapter.getElementSize(this);
26038         this.activeMinSize = this.getMinimumSize();;
26039         this.activeMaxSize = this.getMaximumSize();;
26040         var c1 = size - this.activeMinSize;
26041         var c2 = Math.max(this.activeMaxSize - size, 0);
26042         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26043             this.dd.resetConstraints();
26044             this.dd.setXConstraint(
26045                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26046                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26047             );
26048             this.dd.setYConstraint(0, 0);
26049         }else{
26050             this.dd.resetConstraints();
26051             this.dd.setXConstraint(0, 0);
26052             this.dd.setYConstraint(
26053                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26054                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26055             );
26056          }
26057         this.dragSpecs.startSize = size;
26058         this.dragSpecs.startPoint = [x, y];
26059         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26060     },
26061     
26062     /** 
26063      * @private Called after the drag operation by the DDProxy
26064      */
26065     onEndProxyDrag : function(e){
26066         Roo.get(this.proxy).setDisplayed(false);
26067         var endPoint = Roo.lib.Event.getXY(e);
26068         if(this.overlay){
26069             this.overlay.hide();
26070         }
26071         var newSize;
26072         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26073             newSize = this.dragSpecs.startSize + 
26074                 (this.placement == Roo.SplitBar.LEFT ?
26075                     endPoint[0] - this.dragSpecs.startPoint[0] :
26076                     this.dragSpecs.startPoint[0] - endPoint[0]
26077                 );
26078         }else{
26079             newSize = this.dragSpecs.startSize + 
26080                 (this.placement == Roo.SplitBar.TOP ?
26081                     endPoint[1] - this.dragSpecs.startPoint[1] :
26082                     this.dragSpecs.startPoint[1] - endPoint[1]
26083                 );
26084         }
26085         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26086         if(newSize != this.dragSpecs.startSize){
26087             if(this.fireEvent('beforeapply', this, newSize) !== false){
26088                 this.adapter.setElementSize(this, newSize);
26089                 this.fireEvent("moved", this, newSize);
26090                 this.fireEvent("resize", this, newSize);
26091             }
26092         }
26093     },
26094     
26095     /**
26096      * Get the adapter this SplitBar uses
26097      * @return The adapter object
26098      */
26099     getAdapter : function(){
26100         return this.adapter;
26101     },
26102     
26103     /**
26104      * Set the adapter this SplitBar uses
26105      * @param {Object} adapter A SplitBar adapter object
26106      */
26107     setAdapter : function(adapter){
26108         this.adapter = adapter;
26109         this.adapter.init(this);
26110     },
26111     
26112     /**
26113      * Gets the minimum size for the resizing element
26114      * @return {Number} The minimum size
26115      */
26116     getMinimumSize : function(){
26117         return this.minSize;
26118     },
26119     
26120     /**
26121      * Sets the minimum size for the resizing element
26122      * @param {Number} minSize The minimum size
26123      */
26124     setMinimumSize : function(minSize){
26125         this.minSize = minSize;
26126     },
26127     
26128     /**
26129      * Gets the maximum size for the resizing element
26130      * @return {Number} The maximum size
26131      */
26132     getMaximumSize : function(){
26133         return this.maxSize;
26134     },
26135     
26136     /**
26137      * Sets the maximum size for the resizing element
26138      * @param {Number} maxSize The maximum size
26139      */
26140     setMaximumSize : function(maxSize){
26141         this.maxSize = maxSize;
26142     },
26143     
26144     /**
26145      * Sets the initialize size for the resizing element
26146      * @param {Number} size The initial size
26147      */
26148     setCurrentSize : function(size){
26149         var oldAnimate = this.animate;
26150         this.animate = false;
26151         this.adapter.setElementSize(this, size);
26152         this.animate = oldAnimate;
26153     },
26154     
26155     /**
26156      * Destroy this splitbar. 
26157      * @param {Boolean} removeEl True to remove the element
26158      */
26159     destroy : function(removeEl){
26160         if(this.shim){
26161             this.shim.remove();
26162         }
26163         this.dd.unreg();
26164         this.proxy.parentNode.removeChild(this.proxy);
26165         if(removeEl){
26166             this.el.remove();
26167         }
26168     }
26169 });
26170
26171 /**
26172  * @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.
26173  */
26174 Roo.SplitBar.createProxy = function(dir){
26175     var proxy = new Roo.Element(document.createElement("div"));
26176     proxy.unselectable();
26177     var cls = 'x-splitbar-proxy';
26178     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26179     document.body.appendChild(proxy.dom);
26180     return proxy.dom;
26181 };
26182
26183 /** 
26184  * @class Roo.SplitBar.BasicLayoutAdapter
26185  * Default Adapter. It assumes the splitter and resizing element are not positioned
26186  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26187  */
26188 Roo.SplitBar.BasicLayoutAdapter = function(){
26189 };
26190
26191 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26192     // do nothing for now
26193     init : function(s){
26194     
26195     },
26196     /**
26197      * Called before drag operations to get the current size of the resizing element. 
26198      * @param {Roo.SplitBar} s The SplitBar using this adapter
26199      */
26200      getElementSize : function(s){
26201         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26202             return s.resizingEl.getWidth();
26203         }else{
26204             return s.resizingEl.getHeight();
26205         }
26206     },
26207     
26208     /**
26209      * Called after drag operations to set the size of the resizing element.
26210      * @param {Roo.SplitBar} s The SplitBar using this adapter
26211      * @param {Number} newSize The new size to set
26212      * @param {Function} onComplete A function to be invoked when resizing is complete
26213      */
26214     setElementSize : function(s, newSize, onComplete){
26215         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26216             if(!s.animate){
26217                 s.resizingEl.setWidth(newSize);
26218                 if(onComplete){
26219                     onComplete(s, newSize);
26220                 }
26221             }else{
26222                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26223             }
26224         }else{
26225             
26226             if(!s.animate){
26227                 s.resizingEl.setHeight(newSize);
26228                 if(onComplete){
26229                     onComplete(s, newSize);
26230                 }
26231             }else{
26232                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26233             }
26234         }
26235     }
26236 };
26237
26238 /** 
26239  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26240  * @extends Roo.SplitBar.BasicLayoutAdapter
26241  * Adapter that  moves the splitter element to align with the resized sizing element. 
26242  * Used with an absolute positioned SplitBar.
26243  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26244  * document.body, make sure you assign an id to the body element.
26245  */
26246 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26247     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26248     this.container = Roo.get(container);
26249 };
26250
26251 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26252     init : function(s){
26253         this.basic.init(s);
26254     },
26255     
26256     getElementSize : function(s){
26257         return this.basic.getElementSize(s);
26258     },
26259     
26260     setElementSize : function(s, newSize, onComplete){
26261         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26262     },
26263     
26264     moveSplitter : function(s){
26265         var yes = Roo.SplitBar;
26266         switch(s.placement){
26267             case yes.LEFT:
26268                 s.el.setX(s.resizingEl.getRight());
26269                 break;
26270             case yes.RIGHT:
26271                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26272                 break;
26273             case yes.TOP:
26274                 s.el.setY(s.resizingEl.getBottom());
26275                 break;
26276             case yes.BOTTOM:
26277                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26278                 break;
26279         }
26280     }
26281 };
26282
26283 /**
26284  * Orientation constant - Create a vertical SplitBar
26285  * @static
26286  * @type Number
26287  */
26288 Roo.SplitBar.VERTICAL = 1;
26289
26290 /**
26291  * Orientation constant - Create a horizontal SplitBar
26292  * @static
26293  * @type Number
26294  */
26295 Roo.SplitBar.HORIZONTAL = 2;
26296
26297 /**
26298  * Placement constant - The resizing element is to the left of the splitter element
26299  * @static
26300  * @type Number
26301  */
26302 Roo.SplitBar.LEFT = 1;
26303
26304 /**
26305  * Placement constant - The resizing element is to the right of the splitter element
26306  * @static
26307  * @type Number
26308  */
26309 Roo.SplitBar.RIGHT = 2;
26310
26311 /**
26312  * Placement constant - The resizing element is positioned above the splitter element
26313  * @static
26314  * @type Number
26315  */
26316 Roo.SplitBar.TOP = 3;
26317
26318 /**
26319  * Placement constant - The resizing element is positioned under splitter element
26320  * @static
26321  * @type Number
26322  */
26323 Roo.SplitBar.BOTTOM = 4;
26324 /*
26325  * Based on:
26326  * Ext JS Library 1.1.1
26327  * Copyright(c) 2006-2007, Ext JS, LLC.
26328  *
26329  * Originally Released Under LGPL - original licence link has changed is not relivant.
26330  *
26331  * Fork - LGPL
26332  * <script type="text/javascript">
26333  */
26334
26335 /**
26336  * @class Roo.View
26337  * @extends Roo.util.Observable
26338  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26339  * This class also supports single and multi selection modes. <br>
26340  * Create a data model bound view:
26341  <pre><code>
26342  var store = new Roo.data.Store(...);
26343
26344  var view = new Roo.View({
26345     el : "my-element",
26346     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26347  
26348     singleSelect: true,
26349     selectedClass: "ydataview-selected",
26350     store: store
26351  });
26352
26353  // listen for node click?
26354  view.on("click", function(vw, index, node, e){
26355  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26356  });
26357
26358  // load XML data
26359  dataModel.load("foobar.xml");
26360  </code></pre>
26361  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26362  * <br><br>
26363  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26364  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26365  * 
26366  * Note: old style constructor is still suported (container, template, config)
26367  * 
26368  * @constructor
26369  * Create a new View
26370  * @param {Object} config The config object
26371  * 
26372  */
26373 Roo.View = function(config, depreciated_tpl, depreciated_config){
26374     
26375     this.parent = false;
26376     
26377     if (typeof(depreciated_tpl) == 'undefined') {
26378         // new way.. - universal constructor.
26379         Roo.apply(this, config);
26380         this.el  = Roo.get(this.el);
26381     } else {
26382         // old format..
26383         this.el  = Roo.get(config);
26384         this.tpl = depreciated_tpl;
26385         Roo.apply(this, depreciated_config);
26386     }
26387     this.wrapEl  = this.el.wrap().wrap();
26388     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26389     
26390     
26391     if(typeof(this.tpl) == "string"){
26392         this.tpl = new Roo.Template(this.tpl);
26393     } else {
26394         // support xtype ctors..
26395         this.tpl = new Roo.factory(this.tpl, Roo);
26396     }
26397     
26398     
26399     this.tpl.compile();
26400     
26401     /** @private */
26402     this.addEvents({
26403         /**
26404          * @event beforeclick
26405          * Fires before a click is processed. Returns false to cancel the default action.
26406          * @param {Roo.View} this
26407          * @param {Number} index The index of the target node
26408          * @param {HTMLElement} node The target node
26409          * @param {Roo.EventObject} e The raw event object
26410          */
26411             "beforeclick" : true,
26412         /**
26413          * @event click
26414          * Fires when a template node is clicked.
26415          * @param {Roo.View} this
26416          * @param {Number} index The index of the target node
26417          * @param {HTMLElement} node The target node
26418          * @param {Roo.EventObject} e The raw event object
26419          */
26420             "click" : true,
26421         /**
26422          * @event dblclick
26423          * Fires when a template node is double clicked.
26424          * @param {Roo.View} this
26425          * @param {Number} index The index of the target node
26426          * @param {HTMLElement} node The target node
26427          * @param {Roo.EventObject} e The raw event object
26428          */
26429             "dblclick" : true,
26430         /**
26431          * @event contextmenu
26432          * Fires when a template node is right clicked.
26433          * @param {Roo.View} this
26434          * @param {Number} index The index of the target node
26435          * @param {HTMLElement} node The target node
26436          * @param {Roo.EventObject} e The raw event object
26437          */
26438             "contextmenu" : true,
26439         /**
26440          * @event selectionchange
26441          * Fires when the selected nodes change.
26442          * @param {Roo.View} this
26443          * @param {Array} selections Array of the selected nodes
26444          */
26445             "selectionchange" : true,
26446     
26447         /**
26448          * @event beforeselect
26449          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26450          * @param {Roo.View} this
26451          * @param {HTMLElement} node The node to be selected
26452          * @param {Array} selections Array of currently selected nodes
26453          */
26454             "beforeselect" : true,
26455         /**
26456          * @event preparedata
26457          * Fires on every row to render, to allow you to change the data.
26458          * @param {Roo.View} this
26459          * @param {Object} data to be rendered (change this)
26460          */
26461           "preparedata" : true
26462           
26463           
26464         });
26465
26466
26467
26468     this.el.on({
26469         "click": this.onClick,
26470         "dblclick": this.onDblClick,
26471         "contextmenu": this.onContextMenu,
26472         scope:this
26473     });
26474
26475     this.selections = [];
26476     this.nodes = [];
26477     this.cmp = new Roo.CompositeElementLite([]);
26478     if(this.store){
26479         this.store = Roo.factory(this.store, Roo.data);
26480         this.setStore(this.store, true);
26481     }
26482     
26483     if ( this.footer && this.footer.xtype) {
26484            
26485          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26486         
26487         this.footer.dataSource = this.store;
26488         this.footer.container = fctr;
26489         this.footer = Roo.factory(this.footer, Roo);
26490         fctr.insertFirst(this.el);
26491         
26492         // this is a bit insane - as the paging toolbar seems to detach the el..
26493 //        dom.parentNode.parentNode.parentNode
26494          // they get detached?
26495     }
26496     
26497     
26498     Roo.View.superclass.constructor.call(this);
26499     
26500     
26501 };
26502
26503 Roo.extend(Roo.View, Roo.util.Observable, {
26504     
26505      /**
26506      * @cfg {Roo.data.Store} store Data store to load data from.
26507      */
26508     store : false,
26509     
26510     /**
26511      * @cfg {String|Roo.Element} el The container element.
26512      */
26513     el : '',
26514     
26515     /**
26516      * @cfg {String|Roo.Template} tpl The template used by this View 
26517      */
26518     tpl : false,
26519     /**
26520      * @cfg {String} dataName the named area of the template to use as the data area
26521      *                          Works with domtemplates roo-name="name"
26522      */
26523     dataName: false,
26524     /**
26525      * @cfg {String} selectedClass The css class to add to selected nodes
26526      */
26527     selectedClass : "x-view-selected",
26528      /**
26529      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26530      */
26531     emptyText : "",
26532     
26533     /**
26534      * @cfg {String} text to display on mask (default Loading)
26535      */
26536     mask : false,
26537     /**
26538      * @cfg {Boolean} multiSelect Allow multiple selection
26539      */
26540     multiSelect : false,
26541     /**
26542      * @cfg {Boolean} singleSelect Allow single selection
26543      */
26544     singleSelect:  false,
26545     
26546     /**
26547      * @cfg {Boolean} toggleSelect - selecting 
26548      */
26549     toggleSelect : false,
26550     
26551     /**
26552      * @cfg {Boolean} tickable - selecting 
26553      */
26554     tickable : false,
26555     
26556     /**
26557      * Returns the element this view is bound to.
26558      * @return {Roo.Element}
26559      */
26560     getEl : function(){
26561         return this.wrapEl;
26562     },
26563     
26564     
26565
26566     /**
26567      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26568      */
26569     refresh : function(){
26570         //Roo.log('refresh');
26571         var t = this.tpl;
26572         
26573         // if we are using something like 'domtemplate', then
26574         // the what gets used is:
26575         // t.applySubtemplate(NAME, data, wrapping data..)
26576         // the outer template then get' applied with
26577         //     the store 'extra data'
26578         // and the body get's added to the
26579         //      roo-name="data" node?
26580         //      <span class='roo-tpl-{name}'></span> ?????
26581         
26582         
26583         
26584         this.clearSelections();
26585         this.el.update("");
26586         var html = [];
26587         var records = this.store.getRange();
26588         if(records.length < 1) {
26589             
26590             // is this valid??  = should it render a template??
26591             
26592             this.el.update(this.emptyText);
26593             return;
26594         }
26595         var el = this.el;
26596         if (this.dataName) {
26597             this.el.update(t.apply(this.store.meta)); //????
26598             el = this.el.child('.roo-tpl-' + this.dataName);
26599         }
26600         
26601         for(var i = 0, len = records.length; i < len; i++){
26602             var data = this.prepareData(records[i].data, i, records[i]);
26603             this.fireEvent("preparedata", this, data, i, records[i]);
26604             
26605             var d = Roo.apply({}, data);
26606             
26607             if(this.tickable){
26608                 Roo.apply(d, {'roo-id' : Roo.id()});
26609                 
26610                 var _this = this;
26611             
26612                 Roo.each(this.parent.item, function(item){
26613                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26614                         return;
26615                     }
26616                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26617                 });
26618             }
26619             
26620             html[html.length] = Roo.util.Format.trim(
26621                 this.dataName ?
26622                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26623                     t.apply(d)
26624             );
26625         }
26626         
26627         
26628         
26629         el.update(html.join(""));
26630         this.nodes = el.dom.childNodes;
26631         this.updateIndexes(0);
26632     },
26633     
26634
26635     /**
26636      * Function to override to reformat the data that is sent to
26637      * the template for each node.
26638      * DEPRICATED - use the preparedata event handler.
26639      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26640      * a JSON object for an UpdateManager bound view).
26641      */
26642     prepareData : function(data, index, record)
26643     {
26644         this.fireEvent("preparedata", this, data, index, record);
26645         return data;
26646     },
26647
26648     onUpdate : function(ds, record){
26649         // Roo.log('on update');   
26650         this.clearSelections();
26651         var index = this.store.indexOf(record);
26652         var n = this.nodes[index];
26653         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26654         n.parentNode.removeChild(n);
26655         this.updateIndexes(index, index);
26656     },
26657
26658     
26659     
26660 // --------- FIXME     
26661     onAdd : function(ds, records, index)
26662     {
26663         //Roo.log(['on Add', ds, records, index] );        
26664         this.clearSelections();
26665         if(this.nodes.length == 0){
26666             this.refresh();
26667             return;
26668         }
26669         var n = this.nodes[index];
26670         for(var i = 0, len = records.length; i < len; i++){
26671             var d = this.prepareData(records[i].data, i, records[i]);
26672             if(n){
26673                 this.tpl.insertBefore(n, d);
26674             }else{
26675                 
26676                 this.tpl.append(this.el, d);
26677             }
26678         }
26679         this.updateIndexes(index);
26680     },
26681
26682     onRemove : function(ds, record, index){
26683        // Roo.log('onRemove');
26684         this.clearSelections();
26685         var el = this.dataName  ?
26686             this.el.child('.roo-tpl-' + this.dataName) :
26687             this.el; 
26688         
26689         el.dom.removeChild(this.nodes[index]);
26690         this.updateIndexes(index);
26691     },
26692
26693     /**
26694      * Refresh an individual node.
26695      * @param {Number} index
26696      */
26697     refreshNode : function(index){
26698         this.onUpdate(this.store, this.store.getAt(index));
26699     },
26700
26701     updateIndexes : function(startIndex, endIndex){
26702         var ns = this.nodes;
26703         startIndex = startIndex || 0;
26704         endIndex = endIndex || ns.length - 1;
26705         for(var i = startIndex; i <= endIndex; i++){
26706             ns[i].nodeIndex = i;
26707         }
26708     },
26709
26710     /**
26711      * Changes the data store this view uses and refresh the view.
26712      * @param {Store} store
26713      */
26714     setStore : function(store, initial){
26715         if(!initial && this.store){
26716             this.store.un("datachanged", this.refresh);
26717             this.store.un("add", this.onAdd);
26718             this.store.un("remove", this.onRemove);
26719             this.store.un("update", this.onUpdate);
26720             this.store.un("clear", this.refresh);
26721             this.store.un("beforeload", this.onBeforeLoad);
26722             this.store.un("load", this.onLoad);
26723             this.store.un("loadexception", this.onLoad);
26724         }
26725         if(store){
26726           
26727             store.on("datachanged", this.refresh, this);
26728             store.on("add", this.onAdd, this);
26729             store.on("remove", this.onRemove, this);
26730             store.on("update", this.onUpdate, this);
26731             store.on("clear", this.refresh, this);
26732             store.on("beforeload", this.onBeforeLoad, this);
26733             store.on("load", this.onLoad, this);
26734             store.on("loadexception", this.onLoad, this);
26735         }
26736         
26737         if(store){
26738             this.refresh();
26739         }
26740     },
26741     /**
26742      * onbeforeLoad - masks the loading area.
26743      *
26744      */
26745     onBeforeLoad : function(store,opts)
26746     {
26747          //Roo.log('onBeforeLoad');   
26748         if (!opts.add) {
26749             this.el.update("");
26750         }
26751         this.el.mask(this.mask ? this.mask : "Loading" ); 
26752     },
26753     onLoad : function ()
26754     {
26755         this.el.unmask();
26756     },
26757     
26758
26759     /**
26760      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26761      * @param {HTMLElement} node
26762      * @return {HTMLElement} The template node
26763      */
26764     findItemFromChild : function(node){
26765         var el = this.dataName  ?
26766             this.el.child('.roo-tpl-' + this.dataName,true) :
26767             this.el.dom; 
26768         
26769         if(!node || node.parentNode == el){
26770                     return node;
26771             }
26772             var p = node.parentNode;
26773             while(p && p != el){
26774             if(p.parentNode == el){
26775                 return p;
26776             }
26777             p = p.parentNode;
26778         }
26779             return null;
26780     },
26781
26782     /** @ignore */
26783     onClick : function(e){
26784         var item = this.findItemFromChild(e.getTarget());
26785         if(item){
26786             var index = this.indexOf(item);
26787             if(this.onItemClick(item, index, e) !== false){
26788                 this.fireEvent("click", this, index, item, e);
26789             }
26790         }else{
26791             this.clearSelections();
26792         }
26793     },
26794
26795     /** @ignore */
26796     onContextMenu : function(e){
26797         var item = this.findItemFromChild(e.getTarget());
26798         if(item){
26799             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26800         }
26801     },
26802
26803     /** @ignore */
26804     onDblClick : function(e){
26805         var item = this.findItemFromChild(e.getTarget());
26806         if(item){
26807             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26808         }
26809     },
26810
26811     onItemClick : function(item, index, e)
26812     {
26813         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26814             return false;
26815         }
26816         if (this.toggleSelect) {
26817             var m = this.isSelected(item) ? 'unselect' : 'select';
26818             //Roo.log(m);
26819             var _t = this;
26820             _t[m](item, true, false);
26821             return true;
26822         }
26823         if(this.multiSelect || this.singleSelect){
26824             if(this.multiSelect && e.shiftKey && this.lastSelection){
26825                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26826             }else{
26827                 this.select(item, this.multiSelect && e.ctrlKey);
26828                 this.lastSelection = item;
26829             }
26830             
26831             if(!this.tickable){
26832                 e.preventDefault();
26833             }
26834             
26835         }
26836         return true;
26837     },
26838
26839     /**
26840      * Get the number of selected nodes.
26841      * @return {Number}
26842      */
26843     getSelectionCount : function(){
26844         return this.selections.length;
26845     },
26846
26847     /**
26848      * Get the currently selected nodes.
26849      * @return {Array} An array of HTMLElements
26850      */
26851     getSelectedNodes : function(){
26852         return this.selections;
26853     },
26854
26855     /**
26856      * Get the indexes of the selected nodes.
26857      * @return {Array}
26858      */
26859     getSelectedIndexes : function(){
26860         var indexes = [], s = this.selections;
26861         for(var i = 0, len = s.length; i < len; i++){
26862             indexes.push(s[i].nodeIndex);
26863         }
26864         return indexes;
26865     },
26866
26867     /**
26868      * Clear all selections
26869      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26870      */
26871     clearSelections : function(suppressEvent){
26872         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26873             this.cmp.elements = this.selections;
26874             this.cmp.removeClass(this.selectedClass);
26875             this.selections = [];
26876             if(!suppressEvent){
26877                 this.fireEvent("selectionchange", this, this.selections);
26878             }
26879         }
26880     },
26881
26882     /**
26883      * Returns true if the passed node is selected
26884      * @param {HTMLElement/Number} node The node or node index
26885      * @return {Boolean}
26886      */
26887     isSelected : function(node){
26888         var s = this.selections;
26889         if(s.length < 1){
26890             return false;
26891         }
26892         node = this.getNode(node);
26893         return s.indexOf(node) !== -1;
26894     },
26895
26896     /**
26897      * Selects nodes.
26898      * @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
26899      * @param {Boolean} keepExisting (optional) true to keep existing selections
26900      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26901      */
26902     select : function(nodeInfo, keepExisting, suppressEvent){
26903         if(nodeInfo instanceof Array){
26904             if(!keepExisting){
26905                 this.clearSelections(true);
26906             }
26907             for(var i = 0, len = nodeInfo.length; i < len; i++){
26908                 this.select(nodeInfo[i], true, true);
26909             }
26910             return;
26911         } 
26912         var node = this.getNode(nodeInfo);
26913         if(!node || this.isSelected(node)){
26914             return; // already selected.
26915         }
26916         if(!keepExisting){
26917             this.clearSelections(true);
26918         }
26919         
26920         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26921             Roo.fly(node).addClass(this.selectedClass);
26922             this.selections.push(node);
26923             if(!suppressEvent){
26924                 this.fireEvent("selectionchange", this, this.selections);
26925             }
26926         }
26927         
26928         
26929     },
26930       /**
26931      * Unselects nodes.
26932      * @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
26933      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26934      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26935      */
26936     unselect : function(nodeInfo, keepExisting, suppressEvent)
26937     {
26938         if(nodeInfo instanceof Array){
26939             Roo.each(this.selections, function(s) {
26940                 this.unselect(s, nodeInfo);
26941             }, this);
26942             return;
26943         }
26944         var node = this.getNode(nodeInfo);
26945         if(!node || !this.isSelected(node)){
26946             //Roo.log("not selected");
26947             return; // not selected.
26948         }
26949         // fireevent???
26950         var ns = [];
26951         Roo.each(this.selections, function(s) {
26952             if (s == node ) {
26953                 Roo.fly(node).removeClass(this.selectedClass);
26954
26955                 return;
26956             }
26957             ns.push(s);
26958         },this);
26959         
26960         this.selections= ns;
26961         this.fireEvent("selectionchange", this, this.selections);
26962     },
26963
26964     /**
26965      * Gets a template node.
26966      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26967      * @return {HTMLElement} The node or null if it wasn't found
26968      */
26969     getNode : function(nodeInfo){
26970         if(typeof nodeInfo == "string"){
26971             return document.getElementById(nodeInfo);
26972         }else if(typeof nodeInfo == "number"){
26973             return this.nodes[nodeInfo];
26974         }
26975         return nodeInfo;
26976     },
26977
26978     /**
26979      * Gets a range template nodes.
26980      * @param {Number} startIndex
26981      * @param {Number} endIndex
26982      * @return {Array} An array of nodes
26983      */
26984     getNodes : function(start, end){
26985         var ns = this.nodes;
26986         start = start || 0;
26987         end = typeof end == "undefined" ? ns.length - 1 : end;
26988         var nodes = [];
26989         if(start <= end){
26990             for(var i = start; i <= end; i++){
26991                 nodes.push(ns[i]);
26992             }
26993         } else{
26994             for(var i = start; i >= end; i--){
26995                 nodes.push(ns[i]);
26996             }
26997         }
26998         return nodes;
26999     },
27000
27001     /**
27002      * Finds the index of the passed node
27003      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27004      * @return {Number} The index of the node or -1
27005      */
27006     indexOf : function(node){
27007         node = this.getNode(node);
27008         if(typeof node.nodeIndex == "number"){
27009             return node.nodeIndex;
27010         }
27011         var ns = this.nodes;
27012         for(var i = 0, len = ns.length; i < len; i++){
27013             if(ns[i] == node){
27014                 return i;
27015             }
27016         }
27017         return -1;
27018     }
27019 });
27020 /*
27021  * Based on:
27022  * Ext JS Library 1.1.1
27023  * Copyright(c) 2006-2007, Ext JS, LLC.
27024  *
27025  * Originally Released Under LGPL - original licence link has changed is not relivant.
27026  *
27027  * Fork - LGPL
27028  * <script type="text/javascript">
27029  */
27030
27031 /**
27032  * @class Roo.JsonView
27033  * @extends Roo.View
27034  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27035 <pre><code>
27036 var view = new Roo.JsonView({
27037     container: "my-element",
27038     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27039     multiSelect: true, 
27040     jsonRoot: "data" 
27041 });
27042
27043 // listen for node click?
27044 view.on("click", function(vw, index, node, e){
27045     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27046 });
27047
27048 // direct load of JSON data
27049 view.load("foobar.php");
27050
27051 // Example from my blog list
27052 var tpl = new Roo.Template(
27053     '&lt;div class="entry"&gt;' +
27054     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27055     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27056     "&lt;/div&gt;&lt;hr /&gt;"
27057 );
27058
27059 var moreView = new Roo.JsonView({
27060     container :  "entry-list", 
27061     template : tpl,
27062     jsonRoot: "posts"
27063 });
27064 moreView.on("beforerender", this.sortEntries, this);
27065 moreView.load({
27066     url: "/blog/get-posts.php",
27067     params: "allposts=true",
27068     text: "Loading Blog Entries..."
27069 });
27070 </code></pre>
27071
27072 * Note: old code is supported with arguments : (container, template, config)
27073
27074
27075  * @constructor
27076  * Create a new JsonView
27077  * 
27078  * @param {Object} config The config object
27079  * 
27080  */
27081 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27082     
27083     
27084     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27085
27086     var um = this.el.getUpdateManager();
27087     um.setRenderer(this);
27088     um.on("update", this.onLoad, this);
27089     um.on("failure", this.onLoadException, this);
27090
27091     /**
27092      * @event beforerender
27093      * Fires before rendering of the downloaded JSON data.
27094      * @param {Roo.JsonView} this
27095      * @param {Object} data The JSON data loaded
27096      */
27097     /**
27098      * @event load
27099      * Fires when data is loaded.
27100      * @param {Roo.JsonView} this
27101      * @param {Object} data The JSON data loaded
27102      * @param {Object} response The raw Connect response object
27103      */
27104     /**
27105      * @event loadexception
27106      * Fires when loading fails.
27107      * @param {Roo.JsonView} this
27108      * @param {Object} response The raw Connect response object
27109      */
27110     this.addEvents({
27111         'beforerender' : true,
27112         'load' : true,
27113         'loadexception' : true
27114     });
27115 };
27116 Roo.extend(Roo.JsonView, Roo.View, {
27117     /**
27118      * @type {String} The root property in the loaded JSON object that contains the data
27119      */
27120     jsonRoot : "",
27121
27122     /**
27123      * Refreshes the view.
27124      */
27125     refresh : function(){
27126         this.clearSelections();
27127         this.el.update("");
27128         var html = [];
27129         var o = this.jsonData;
27130         if(o && o.length > 0){
27131             for(var i = 0, len = o.length; i < len; i++){
27132                 var data = this.prepareData(o[i], i, o);
27133                 html[html.length] = this.tpl.apply(data);
27134             }
27135         }else{
27136             html.push(this.emptyText);
27137         }
27138         this.el.update(html.join(""));
27139         this.nodes = this.el.dom.childNodes;
27140         this.updateIndexes(0);
27141     },
27142
27143     /**
27144      * 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.
27145      * @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:
27146      <pre><code>
27147      view.load({
27148          url: "your-url.php",
27149          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27150          callback: yourFunction,
27151          scope: yourObject, //(optional scope)
27152          discardUrl: false,
27153          nocache: false,
27154          text: "Loading...",
27155          timeout: 30,
27156          scripts: false
27157      });
27158      </code></pre>
27159      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27160      * 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.
27161      * @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}
27162      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27163      * @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.
27164      */
27165     load : function(){
27166         var um = this.el.getUpdateManager();
27167         um.update.apply(um, arguments);
27168     },
27169
27170     // note - render is a standard framework call...
27171     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27172     render : function(el, response){
27173         
27174         this.clearSelections();
27175         this.el.update("");
27176         var o;
27177         try{
27178             if (response != '') {
27179                 o = Roo.util.JSON.decode(response.responseText);
27180                 if(this.jsonRoot){
27181                     
27182                     o = o[this.jsonRoot];
27183                 }
27184             }
27185         } catch(e){
27186         }
27187         /**
27188          * The current JSON data or null
27189          */
27190         this.jsonData = o;
27191         this.beforeRender();
27192         this.refresh();
27193     },
27194
27195 /**
27196  * Get the number of records in the current JSON dataset
27197  * @return {Number}
27198  */
27199     getCount : function(){
27200         return this.jsonData ? this.jsonData.length : 0;
27201     },
27202
27203 /**
27204  * Returns the JSON object for the specified node(s)
27205  * @param {HTMLElement/Array} node The node or an array of nodes
27206  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27207  * you get the JSON object for the node
27208  */
27209     getNodeData : function(node){
27210         if(node instanceof Array){
27211             var data = [];
27212             for(var i = 0, len = node.length; i < len; i++){
27213                 data.push(this.getNodeData(node[i]));
27214             }
27215             return data;
27216         }
27217         return this.jsonData[this.indexOf(node)] || null;
27218     },
27219
27220     beforeRender : function(){
27221         this.snapshot = this.jsonData;
27222         if(this.sortInfo){
27223             this.sort.apply(this, this.sortInfo);
27224         }
27225         this.fireEvent("beforerender", this, this.jsonData);
27226     },
27227
27228     onLoad : function(el, o){
27229         this.fireEvent("load", this, this.jsonData, o);
27230     },
27231
27232     onLoadException : function(el, o){
27233         this.fireEvent("loadexception", this, o);
27234     },
27235
27236 /**
27237  * Filter the data by a specific property.
27238  * @param {String} property A property on your JSON objects
27239  * @param {String/RegExp} value Either string that the property values
27240  * should start with, or a RegExp to test against the property
27241  */
27242     filter : function(property, value){
27243         if(this.jsonData){
27244             var data = [];
27245             var ss = this.snapshot;
27246             if(typeof value == "string"){
27247                 var vlen = value.length;
27248                 if(vlen == 0){
27249                     this.clearFilter();
27250                     return;
27251                 }
27252                 value = value.toLowerCase();
27253                 for(var i = 0, len = ss.length; i < len; i++){
27254                     var o = ss[i];
27255                     if(o[property].substr(0, vlen).toLowerCase() == value){
27256                         data.push(o);
27257                     }
27258                 }
27259             } else if(value.exec){ // regex?
27260                 for(var i = 0, len = ss.length; i < len; i++){
27261                     var o = ss[i];
27262                     if(value.test(o[property])){
27263                         data.push(o);
27264                     }
27265                 }
27266             } else{
27267                 return;
27268             }
27269             this.jsonData = data;
27270             this.refresh();
27271         }
27272     },
27273
27274 /**
27275  * Filter by a function. The passed function will be called with each
27276  * object in the current dataset. If the function returns true the value is kept,
27277  * otherwise it is filtered.
27278  * @param {Function} fn
27279  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27280  */
27281     filterBy : function(fn, scope){
27282         if(this.jsonData){
27283             var data = [];
27284             var ss = this.snapshot;
27285             for(var i = 0, len = ss.length; i < len; i++){
27286                 var o = ss[i];
27287                 if(fn.call(scope || this, o)){
27288                     data.push(o);
27289                 }
27290             }
27291             this.jsonData = data;
27292             this.refresh();
27293         }
27294     },
27295
27296 /**
27297  * Clears the current filter.
27298  */
27299     clearFilter : function(){
27300         if(this.snapshot && this.jsonData != this.snapshot){
27301             this.jsonData = this.snapshot;
27302             this.refresh();
27303         }
27304     },
27305
27306
27307 /**
27308  * Sorts the data for this view and refreshes it.
27309  * @param {String} property A property on your JSON objects to sort on
27310  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27311  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27312  */
27313     sort : function(property, dir, sortType){
27314         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27315         if(this.jsonData){
27316             var p = property;
27317             var dsc = dir && dir.toLowerCase() == "desc";
27318             var f = function(o1, o2){
27319                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27320                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27321                 ;
27322                 if(v1 < v2){
27323                     return dsc ? +1 : -1;
27324                 } else if(v1 > v2){
27325                     return dsc ? -1 : +1;
27326                 } else{
27327                     return 0;
27328                 }
27329             };
27330             this.jsonData.sort(f);
27331             this.refresh();
27332             if(this.jsonData != this.snapshot){
27333                 this.snapshot.sort(f);
27334             }
27335         }
27336     }
27337 });/*
27338  * Based on:
27339  * Ext JS Library 1.1.1
27340  * Copyright(c) 2006-2007, Ext JS, LLC.
27341  *
27342  * Originally Released Under LGPL - original licence link has changed is not relivant.
27343  *
27344  * Fork - LGPL
27345  * <script type="text/javascript">
27346  */
27347  
27348
27349 /**
27350  * @class Roo.ColorPalette
27351  * @extends Roo.Component
27352  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27353  * Here's an example of typical usage:
27354  * <pre><code>
27355 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27356 cp.render('my-div');
27357
27358 cp.on('select', function(palette, selColor){
27359     // do something with selColor
27360 });
27361 </code></pre>
27362  * @constructor
27363  * Create a new ColorPalette
27364  * @param {Object} config The config object
27365  */
27366 Roo.ColorPalette = function(config){
27367     Roo.ColorPalette.superclass.constructor.call(this, config);
27368     this.addEvents({
27369         /**
27370              * @event select
27371              * Fires when a color is selected
27372              * @param {ColorPalette} this
27373              * @param {String} color The 6-digit color hex code (without the # symbol)
27374              */
27375         select: true
27376     });
27377
27378     if(this.handler){
27379         this.on("select", this.handler, this.scope, true);
27380     }
27381 };
27382 Roo.extend(Roo.ColorPalette, Roo.Component, {
27383     /**
27384      * @cfg {String} itemCls
27385      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27386      */
27387     itemCls : "x-color-palette",
27388     /**
27389      * @cfg {String} value
27390      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27391      * the hex codes are case-sensitive.
27392      */
27393     value : null,
27394     clickEvent:'click',
27395     // private
27396     ctype: "Roo.ColorPalette",
27397
27398     /**
27399      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27400      */
27401     allowReselect : false,
27402
27403     /**
27404      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27405      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27406      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27407      * of colors with the width setting until the box is symmetrical.</p>
27408      * <p>You can override individual colors if needed:</p>
27409      * <pre><code>
27410 var cp = new Roo.ColorPalette();
27411 cp.colors[0] = "FF0000";  // change the first box to red
27412 </code></pre>
27413
27414 Or you can provide a custom array of your own for complete control:
27415 <pre><code>
27416 var cp = new Roo.ColorPalette();
27417 cp.colors = ["000000", "993300", "333300"];
27418 </code></pre>
27419      * @type Array
27420      */
27421     colors : [
27422         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27423         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27424         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27425         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27426         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27427     ],
27428
27429     // private
27430     onRender : function(container, position){
27431         var t = new Roo.MasterTemplate(
27432             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27433         );
27434         var c = this.colors;
27435         for(var i = 0, len = c.length; i < len; i++){
27436             t.add([c[i]]);
27437         }
27438         var el = document.createElement("div");
27439         el.className = this.itemCls;
27440         t.overwrite(el);
27441         container.dom.insertBefore(el, position);
27442         this.el = Roo.get(el);
27443         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27444         if(this.clickEvent != 'click'){
27445             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27446         }
27447     },
27448
27449     // private
27450     afterRender : function(){
27451         Roo.ColorPalette.superclass.afterRender.call(this);
27452         if(this.value){
27453             var s = this.value;
27454             this.value = null;
27455             this.select(s);
27456         }
27457     },
27458
27459     // private
27460     handleClick : function(e, t){
27461         e.preventDefault();
27462         if(!this.disabled){
27463             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27464             this.select(c.toUpperCase());
27465         }
27466     },
27467
27468     /**
27469      * Selects the specified color in the palette (fires the select event)
27470      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27471      */
27472     select : function(color){
27473         color = color.replace("#", "");
27474         if(color != this.value || this.allowReselect){
27475             var el = this.el;
27476             if(this.value){
27477                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27478             }
27479             el.child("a.color-"+color).addClass("x-color-palette-sel");
27480             this.value = color;
27481             this.fireEvent("select", this, color);
27482         }
27483     }
27484 });/*
27485  * Based on:
27486  * Ext JS Library 1.1.1
27487  * Copyright(c) 2006-2007, Ext JS, LLC.
27488  *
27489  * Originally Released Under LGPL - original licence link has changed is not relivant.
27490  *
27491  * Fork - LGPL
27492  * <script type="text/javascript">
27493  */
27494  
27495 /**
27496  * @class Roo.DatePicker
27497  * @extends Roo.Component
27498  * Simple date picker class.
27499  * @constructor
27500  * Create a new DatePicker
27501  * @param {Object} config The config object
27502  */
27503 Roo.DatePicker = function(config){
27504     Roo.DatePicker.superclass.constructor.call(this, config);
27505
27506     this.value = config && config.value ?
27507                  config.value.clearTime() : new Date().clearTime();
27508
27509     this.addEvents({
27510         /**
27511              * @event select
27512              * Fires when a date is selected
27513              * @param {DatePicker} this
27514              * @param {Date} date The selected date
27515              */
27516         'select': true,
27517         /**
27518              * @event monthchange
27519              * Fires when the displayed month changes 
27520              * @param {DatePicker} this
27521              * @param {Date} date The selected month
27522              */
27523         'monthchange': true
27524     });
27525
27526     if(this.handler){
27527         this.on("select", this.handler,  this.scope || this);
27528     }
27529     // build the disabledDatesRE
27530     if(!this.disabledDatesRE && this.disabledDates){
27531         var dd = this.disabledDates;
27532         var re = "(?:";
27533         for(var i = 0; i < dd.length; i++){
27534             re += dd[i];
27535             if(i != dd.length-1) {
27536                 re += "|";
27537             }
27538         }
27539         this.disabledDatesRE = new RegExp(re + ")");
27540     }
27541 };
27542
27543 Roo.extend(Roo.DatePicker, Roo.Component, {
27544     /**
27545      * @cfg {String} todayText
27546      * The text to display on the button that selects the current date (defaults to "Today")
27547      */
27548     todayText : "Today",
27549     /**
27550      * @cfg {String} okText
27551      * The text to display on the ok button
27552      */
27553     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27554     /**
27555      * @cfg {String} cancelText
27556      * The text to display on the cancel button
27557      */
27558     cancelText : "Cancel",
27559     /**
27560      * @cfg {String} todayTip
27561      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27562      */
27563     todayTip : "{0} (Spacebar)",
27564     /**
27565      * @cfg {Date} minDate
27566      * Minimum allowable date (JavaScript date object, defaults to null)
27567      */
27568     minDate : null,
27569     /**
27570      * @cfg {Date} maxDate
27571      * Maximum allowable date (JavaScript date object, defaults to null)
27572      */
27573     maxDate : null,
27574     /**
27575      * @cfg {String} minText
27576      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27577      */
27578     minText : "This date is before the minimum date",
27579     /**
27580      * @cfg {String} maxText
27581      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27582      */
27583     maxText : "This date is after the maximum date",
27584     /**
27585      * @cfg {String} format
27586      * The default date format string which can be overriden for localization support.  The format must be
27587      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27588      */
27589     format : "m/d/y",
27590     /**
27591      * @cfg {Array} disabledDays
27592      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27593      */
27594     disabledDays : null,
27595     /**
27596      * @cfg {String} disabledDaysText
27597      * The tooltip to display when the date falls on a disabled day (defaults to "")
27598      */
27599     disabledDaysText : "",
27600     /**
27601      * @cfg {RegExp} disabledDatesRE
27602      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27603      */
27604     disabledDatesRE : null,
27605     /**
27606      * @cfg {String} disabledDatesText
27607      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27608      */
27609     disabledDatesText : "",
27610     /**
27611      * @cfg {Boolean} constrainToViewport
27612      * True to constrain the date picker to the viewport (defaults to true)
27613      */
27614     constrainToViewport : true,
27615     /**
27616      * @cfg {Array} monthNames
27617      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27618      */
27619     monthNames : Date.monthNames,
27620     /**
27621      * @cfg {Array} dayNames
27622      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27623      */
27624     dayNames : Date.dayNames,
27625     /**
27626      * @cfg {String} nextText
27627      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27628      */
27629     nextText: 'Next Month (Control+Right)',
27630     /**
27631      * @cfg {String} prevText
27632      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27633      */
27634     prevText: 'Previous Month (Control+Left)',
27635     /**
27636      * @cfg {String} monthYearText
27637      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27638      */
27639     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27640     /**
27641      * @cfg {Number} startDay
27642      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27643      */
27644     startDay : 0,
27645     /**
27646      * @cfg {Bool} showClear
27647      * Show a clear button (usefull for date form elements that can be blank.)
27648      */
27649     
27650     showClear: false,
27651     
27652     /**
27653      * Sets the value of the date field
27654      * @param {Date} value The date to set
27655      */
27656     setValue : function(value){
27657         var old = this.value;
27658         
27659         if (typeof(value) == 'string') {
27660          
27661             value = Date.parseDate(value, this.format);
27662         }
27663         if (!value) {
27664             value = new Date();
27665         }
27666         
27667         this.value = value.clearTime(true);
27668         if(this.el){
27669             this.update(this.value);
27670         }
27671     },
27672
27673     /**
27674      * Gets the current selected value of the date field
27675      * @return {Date} The selected date
27676      */
27677     getValue : function(){
27678         return this.value;
27679     },
27680
27681     // private
27682     focus : function(){
27683         if(this.el){
27684             this.update(this.activeDate);
27685         }
27686     },
27687
27688     // privateval
27689     onRender : function(container, position){
27690         
27691         var m = [
27692              '<table cellspacing="0">',
27693                 '<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>',
27694                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27695         var dn = this.dayNames;
27696         for(var i = 0; i < 7; i++){
27697             var d = this.startDay+i;
27698             if(d > 6){
27699                 d = d-7;
27700             }
27701             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27702         }
27703         m[m.length] = "</tr></thead><tbody><tr>";
27704         for(var i = 0; i < 42; i++) {
27705             if(i % 7 == 0 && i != 0){
27706                 m[m.length] = "</tr><tr>";
27707             }
27708             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27709         }
27710         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27711             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27712
27713         var el = document.createElement("div");
27714         el.className = "x-date-picker";
27715         el.innerHTML = m.join("");
27716
27717         container.dom.insertBefore(el, position);
27718
27719         this.el = Roo.get(el);
27720         this.eventEl = Roo.get(el.firstChild);
27721
27722         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27723             handler: this.showPrevMonth,
27724             scope: this,
27725             preventDefault:true,
27726             stopDefault:true
27727         });
27728
27729         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27730             handler: this.showNextMonth,
27731             scope: this,
27732             preventDefault:true,
27733             stopDefault:true
27734         });
27735
27736         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27737
27738         this.monthPicker = this.el.down('div.x-date-mp');
27739         this.monthPicker.enableDisplayMode('block');
27740         
27741         var kn = new Roo.KeyNav(this.eventEl, {
27742             "left" : function(e){
27743                 e.ctrlKey ?
27744                     this.showPrevMonth() :
27745                     this.update(this.activeDate.add("d", -1));
27746             },
27747
27748             "right" : function(e){
27749                 e.ctrlKey ?
27750                     this.showNextMonth() :
27751                     this.update(this.activeDate.add("d", 1));
27752             },
27753
27754             "up" : function(e){
27755                 e.ctrlKey ?
27756                     this.showNextYear() :
27757                     this.update(this.activeDate.add("d", -7));
27758             },
27759
27760             "down" : function(e){
27761                 e.ctrlKey ?
27762                     this.showPrevYear() :
27763                     this.update(this.activeDate.add("d", 7));
27764             },
27765
27766             "pageUp" : function(e){
27767                 this.showNextMonth();
27768             },
27769
27770             "pageDown" : function(e){
27771                 this.showPrevMonth();
27772             },
27773
27774             "enter" : function(e){
27775                 e.stopPropagation();
27776                 return true;
27777             },
27778
27779             scope : this
27780         });
27781
27782         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27783
27784         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27785
27786         this.el.unselectable();
27787         
27788         this.cells = this.el.select("table.x-date-inner tbody td");
27789         this.textNodes = this.el.query("table.x-date-inner tbody span");
27790
27791         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27792             text: "&#160;",
27793             tooltip: this.monthYearText
27794         });
27795
27796         this.mbtn.on('click', this.showMonthPicker, this);
27797         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27798
27799
27800         var today = (new Date()).dateFormat(this.format);
27801         
27802         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27803         if (this.showClear) {
27804             baseTb.add( new Roo.Toolbar.Fill());
27805         }
27806         baseTb.add({
27807             text: String.format(this.todayText, today),
27808             tooltip: String.format(this.todayTip, today),
27809             handler: this.selectToday,
27810             scope: this
27811         });
27812         
27813         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27814             
27815         //});
27816         if (this.showClear) {
27817             
27818             baseTb.add( new Roo.Toolbar.Fill());
27819             baseTb.add({
27820                 text: '&#160;',
27821                 cls: 'x-btn-icon x-btn-clear',
27822                 handler: function() {
27823                     //this.value = '';
27824                     this.fireEvent("select", this, '');
27825                 },
27826                 scope: this
27827             });
27828         }
27829         
27830         
27831         if(Roo.isIE){
27832             this.el.repaint();
27833         }
27834         this.update(this.value);
27835     },
27836
27837     createMonthPicker : function(){
27838         if(!this.monthPicker.dom.firstChild){
27839             var buf = ['<table border="0" cellspacing="0">'];
27840             for(var i = 0; i < 6; i++){
27841                 buf.push(
27842                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27843                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27844                     i == 0 ?
27845                     '<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>' :
27846                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27847                 );
27848             }
27849             buf.push(
27850                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27851                     this.okText,
27852                     '</button><button type="button" class="x-date-mp-cancel">',
27853                     this.cancelText,
27854                     '</button></td></tr>',
27855                 '</table>'
27856             );
27857             this.monthPicker.update(buf.join(''));
27858             this.monthPicker.on('click', this.onMonthClick, this);
27859             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27860
27861             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27862             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27863
27864             this.mpMonths.each(function(m, a, i){
27865                 i += 1;
27866                 if((i%2) == 0){
27867                     m.dom.xmonth = 5 + Math.round(i * .5);
27868                 }else{
27869                     m.dom.xmonth = Math.round((i-1) * .5);
27870                 }
27871             });
27872         }
27873     },
27874
27875     showMonthPicker : function(){
27876         this.createMonthPicker();
27877         var size = this.el.getSize();
27878         this.monthPicker.setSize(size);
27879         this.monthPicker.child('table').setSize(size);
27880
27881         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27882         this.updateMPMonth(this.mpSelMonth);
27883         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27884         this.updateMPYear(this.mpSelYear);
27885
27886         this.monthPicker.slideIn('t', {duration:.2});
27887     },
27888
27889     updateMPYear : function(y){
27890         this.mpyear = y;
27891         var ys = this.mpYears.elements;
27892         for(var i = 1; i <= 10; i++){
27893             var td = ys[i-1], y2;
27894             if((i%2) == 0){
27895                 y2 = y + Math.round(i * .5);
27896                 td.firstChild.innerHTML = y2;
27897                 td.xyear = y2;
27898             }else{
27899                 y2 = y - (5-Math.round(i * .5));
27900                 td.firstChild.innerHTML = y2;
27901                 td.xyear = y2;
27902             }
27903             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27904         }
27905     },
27906
27907     updateMPMonth : function(sm){
27908         this.mpMonths.each(function(m, a, i){
27909             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27910         });
27911     },
27912
27913     selectMPMonth: function(m){
27914         
27915     },
27916
27917     onMonthClick : function(e, t){
27918         e.stopEvent();
27919         var el = new Roo.Element(t), pn;
27920         if(el.is('button.x-date-mp-cancel')){
27921             this.hideMonthPicker();
27922         }
27923         else if(el.is('button.x-date-mp-ok')){
27924             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27925             this.hideMonthPicker();
27926         }
27927         else if(pn = el.up('td.x-date-mp-month', 2)){
27928             this.mpMonths.removeClass('x-date-mp-sel');
27929             pn.addClass('x-date-mp-sel');
27930             this.mpSelMonth = pn.dom.xmonth;
27931         }
27932         else if(pn = el.up('td.x-date-mp-year', 2)){
27933             this.mpYears.removeClass('x-date-mp-sel');
27934             pn.addClass('x-date-mp-sel');
27935             this.mpSelYear = pn.dom.xyear;
27936         }
27937         else if(el.is('a.x-date-mp-prev')){
27938             this.updateMPYear(this.mpyear-10);
27939         }
27940         else if(el.is('a.x-date-mp-next')){
27941             this.updateMPYear(this.mpyear+10);
27942         }
27943     },
27944
27945     onMonthDblClick : function(e, t){
27946         e.stopEvent();
27947         var el = new Roo.Element(t), pn;
27948         if(pn = el.up('td.x-date-mp-month', 2)){
27949             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27950             this.hideMonthPicker();
27951         }
27952         else if(pn = el.up('td.x-date-mp-year', 2)){
27953             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27954             this.hideMonthPicker();
27955         }
27956     },
27957
27958     hideMonthPicker : function(disableAnim){
27959         if(this.monthPicker){
27960             if(disableAnim === true){
27961                 this.monthPicker.hide();
27962             }else{
27963                 this.monthPicker.slideOut('t', {duration:.2});
27964             }
27965         }
27966     },
27967
27968     // private
27969     showPrevMonth : function(e){
27970         this.update(this.activeDate.add("mo", -1));
27971     },
27972
27973     // private
27974     showNextMonth : function(e){
27975         this.update(this.activeDate.add("mo", 1));
27976     },
27977
27978     // private
27979     showPrevYear : function(){
27980         this.update(this.activeDate.add("y", -1));
27981     },
27982
27983     // private
27984     showNextYear : function(){
27985         this.update(this.activeDate.add("y", 1));
27986     },
27987
27988     // private
27989     handleMouseWheel : function(e){
27990         var delta = e.getWheelDelta();
27991         if(delta > 0){
27992             this.showPrevMonth();
27993             e.stopEvent();
27994         } else if(delta < 0){
27995             this.showNextMonth();
27996             e.stopEvent();
27997         }
27998     },
27999
28000     // private
28001     handleDateClick : function(e, t){
28002         e.stopEvent();
28003         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28004             this.setValue(new Date(t.dateValue));
28005             this.fireEvent("select", this, this.value);
28006         }
28007     },
28008
28009     // private
28010     selectToday : function(){
28011         this.setValue(new Date().clearTime());
28012         this.fireEvent("select", this, this.value);
28013     },
28014
28015     // private
28016     update : function(date)
28017     {
28018         var vd = this.activeDate;
28019         this.activeDate = date;
28020         if(vd && this.el){
28021             var t = date.getTime();
28022             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28023                 this.cells.removeClass("x-date-selected");
28024                 this.cells.each(function(c){
28025                    if(c.dom.firstChild.dateValue == t){
28026                        c.addClass("x-date-selected");
28027                        setTimeout(function(){
28028                             try{c.dom.firstChild.focus();}catch(e){}
28029                        }, 50);
28030                        return false;
28031                    }
28032                 });
28033                 return;
28034             }
28035         }
28036         
28037         var days = date.getDaysInMonth();
28038         var firstOfMonth = date.getFirstDateOfMonth();
28039         var startingPos = firstOfMonth.getDay()-this.startDay;
28040
28041         if(startingPos <= this.startDay){
28042             startingPos += 7;
28043         }
28044
28045         var pm = date.add("mo", -1);
28046         var prevStart = pm.getDaysInMonth()-startingPos;
28047
28048         var cells = this.cells.elements;
28049         var textEls = this.textNodes;
28050         days += startingPos;
28051
28052         // convert everything to numbers so it's fast
28053         var day = 86400000;
28054         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28055         var today = new Date().clearTime().getTime();
28056         var sel = date.clearTime().getTime();
28057         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28058         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28059         var ddMatch = this.disabledDatesRE;
28060         var ddText = this.disabledDatesText;
28061         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28062         var ddaysText = this.disabledDaysText;
28063         var format = this.format;
28064
28065         var setCellClass = function(cal, cell){
28066             cell.title = "";
28067             var t = d.getTime();
28068             cell.firstChild.dateValue = t;
28069             if(t == today){
28070                 cell.className += " x-date-today";
28071                 cell.title = cal.todayText;
28072             }
28073             if(t == sel){
28074                 cell.className += " x-date-selected";
28075                 setTimeout(function(){
28076                     try{cell.firstChild.focus();}catch(e){}
28077                 }, 50);
28078             }
28079             // disabling
28080             if(t < min) {
28081                 cell.className = " x-date-disabled";
28082                 cell.title = cal.minText;
28083                 return;
28084             }
28085             if(t > max) {
28086                 cell.className = " x-date-disabled";
28087                 cell.title = cal.maxText;
28088                 return;
28089             }
28090             if(ddays){
28091                 if(ddays.indexOf(d.getDay()) != -1){
28092                     cell.title = ddaysText;
28093                     cell.className = " x-date-disabled";
28094                 }
28095             }
28096             if(ddMatch && format){
28097                 var fvalue = d.dateFormat(format);
28098                 if(ddMatch.test(fvalue)){
28099                     cell.title = ddText.replace("%0", fvalue);
28100                     cell.className = " x-date-disabled";
28101                 }
28102             }
28103         };
28104
28105         var i = 0;
28106         for(; i < startingPos; i++) {
28107             textEls[i].innerHTML = (++prevStart);
28108             d.setDate(d.getDate()+1);
28109             cells[i].className = "x-date-prevday";
28110             setCellClass(this, cells[i]);
28111         }
28112         for(; i < days; i++){
28113             intDay = i - startingPos + 1;
28114             textEls[i].innerHTML = (intDay);
28115             d.setDate(d.getDate()+1);
28116             cells[i].className = "x-date-active";
28117             setCellClass(this, cells[i]);
28118         }
28119         var extraDays = 0;
28120         for(; i < 42; i++) {
28121              textEls[i].innerHTML = (++extraDays);
28122              d.setDate(d.getDate()+1);
28123              cells[i].className = "x-date-nextday";
28124              setCellClass(this, cells[i]);
28125         }
28126
28127         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28128         this.fireEvent('monthchange', this, date);
28129         
28130         if(!this.internalRender){
28131             var main = this.el.dom.firstChild;
28132             var w = main.offsetWidth;
28133             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28134             Roo.fly(main).setWidth(w);
28135             this.internalRender = true;
28136             // opera does not respect the auto grow header center column
28137             // then, after it gets a width opera refuses to recalculate
28138             // without a second pass
28139             if(Roo.isOpera && !this.secondPass){
28140                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28141                 this.secondPass = true;
28142                 this.update.defer(10, this, [date]);
28143             }
28144         }
28145         
28146         
28147     }
28148 });        /*
28149  * Based on:
28150  * Ext JS Library 1.1.1
28151  * Copyright(c) 2006-2007, Ext JS, LLC.
28152  *
28153  * Originally Released Under LGPL - original licence link has changed is not relivant.
28154  *
28155  * Fork - LGPL
28156  * <script type="text/javascript">
28157  */
28158 /**
28159  * @class Roo.TabPanel
28160  * @extends Roo.util.Observable
28161  * A lightweight tab container.
28162  * <br><br>
28163  * Usage:
28164  * <pre><code>
28165 // basic tabs 1, built from existing content
28166 var tabs = new Roo.TabPanel("tabs1");
28167 tabs.addTab("script", "View Script");
28168 tabs.addTab("markup", "View Markup");
28169 tabs.activate("script");
28170
28171 // more advanced tabs, built from javascript
28172 var jtabs = new Roo.TabPanel("jtabs");
28173 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28174
28175 // set up the UpdateManager
28176 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28177 var updater = tab2.getUpdateManager();
28178 updater.setDefaultUrl("ajax1.htm");
28179 tab2.on('activate', updater.refresh, updater, true);
28180
28181 // Use setUrl for Ajax loading
28182 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28183 tab3.setUrl("ajax2.htm", null, true);
28184
28185 // Disabled tab
28186 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28187 tab4.disable();
28188
28189 jtabs.activate("jtabs-1");
28190  * </code></pre>
28191  * @constructor
28192  * Create a new TabPanel.
28193  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28194  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28195  */
28196 Roo.TabPanel = function(container, config){
28197     /**
28198     * The container element for this TabPanel.
28199     * @type Roo.Element
28200     */
28201     this.el = Roo.get(container, true);
28202     if(config){
28203         if(typeof config == "boolean"){
28204             this.tabPosition = config ? "bottom" : "top";
28205         }else{
28206             Roo.apply(this, config);
28207         }
28208     }
28209     if(this.tabPosition == "bottom"){
28210         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28211         this.el.addClass("x-tabs-bottom");
28212     }
28213     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28214     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28215     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28216     if(Roo.isIE){
28217         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28218     }
28219     if(this.tabPosition != "bottom"){
28220         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28221          * @type Roo.Element
28222          */
28223         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28224         this.el.addClass("x-tabs-top");
28225     }
28226     this.items = [];
28227
28228     this.bodyEl.setStyle("position", "relative");
28229
28230     this.active = null;
28231     this.activateDelegate = this.activate.createDelegate(this);
28232
28233     this.addEvents({
28234         /**
28235          * @event tabchange
28236          * Fires when the active tab changes
28237          * @param {Roo.TabPanel} this
28238          * @param {Roo.TabPanelItem} activePanel The new active tab
28239          */
28240         "tabchange": true,
28241         /**
28242          * @event beforetabchange
28243          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28244          * @param {Roo.TabPanel} this
28245          * @param {Object} e Set cancel to true on this object to cancel the tab change
28246          * @param {Roo.TabPanelItem} tab The tab being changed to
28247          */
28248         "beforetabchange" : true
28249     });
28250
28251     Roo.EventManager.onWindowResize(this.onResize, this);
28252     this.cpad = this.el.getPadding("lr");
28253     this.hiddenCount = 0;
28254
28255
28256     // toolbar on the tabbar support...
28257     if (this.toolbar) {
28258         var tcfg = this.toolbar;
28259         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28260         this.toolbar = new Roo.Toolbar(tcfg);
28261         if (Roo.isSafari) {
28262             var tbl = tcfg.container.child('table', true);
28263             tbl.setAttribute('width', '100%');
28264         }
28265         
28266     }
28267    
28268
28269
28270     Roo.TabPanel.superclass.constructor.call(this);
28271 };
28272
28273 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28274     /*
28275      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28276      */
28277     tabPosition : "top",
28278     /*
28279      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28280      */
28281     currentTabWidth : 0,
28282     /*
28283      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28284      */
28285     minTabWidth : 40,
28286     /*
28287      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28288      */
28289     maxTabWidth : 250,
28290     /*
28291      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28292      */
28293     preferredTabWidth : 175,
28294     /*
28295      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28296      */
28297     resizeTabs : false,
28298     /*
28299      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28300      */
28301     monitorResize : true,
28302     /*
28303      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28304      */
28305     toolbar : false,
28306
28307     /**
28308      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28309      * @param {String} id The id of the div to use <b>or create</b>
28310      * @param {String} text The text for the tab
28311      * @param {String} content (optional) Content to put in the TabPanelItem body
28312      * @param {Boolean} closable (optional) True to create a close icon on the tab
28313      * @return {Roo.TabPanelItem} The created TabPanelItem
28314      */
28315     addTab : function(id, text, content, closable){
28316         var item = new Roo.TabPanelItem(this, id, text, closable);
28317         this.addTabItem(item);
28318         if(content){
28319             item.setContent(content);
28320         }
28321         return item;
28322     },
28323
28324     /**
28325      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28326      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28327      * @return {Roo.TabPanelItem}
28328      */
28329     getTab : function(id){
28330         return this.items[id];
28331     },
28332
28333     /**
28334      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28335      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28336      */
28337     hideTab : function(id){
28338         var t = this.items[id];
28339         if(!t.isHidden()){
28340            t.setHidden(true);
28341            this.hiddenCount++;
28342            this.autoSizeTabs();
28343         }
28344     },
28345
28346     /**
28347      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28348      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28349      */
28350     unhideTab : function(id){
28351         var t = this.items[id];
28352         if(t.isHidden()){
28353            t.setHidden(false);
28354            this.hiddenCount--;
28355            this.autoSizeTabs();
28356         }
28357     },
28358
28359     /**
28360      * Adds an existing {@link Roo.TabPanelItem}.
28361      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28362      */
28363     addTabItem : function(item){
28364         this.items[item.id] = item;
28365         this.items.push(item);
28366         if(this.resizeTabs){
28367            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28368            this.autoSizeTabs();
28369         }else{
28370             item.autoSize();
28371         }
28372     },
28373
28374     /**
28375      * Removes a {@link Roo.TabPanelItem}.
28376      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28377      */
28378     removeTab : function(id){
28379         var items = this.items;
28380         var tab = items[id];
28381         if(!tab) { return; }
28382         var index = items.indexOf(tab);
28383         if(this.active == tab && items.length > 1){
28384             var newTab = this.getNextAvailable(index);
28385             if(newTab) {
28386                 newTab.activate();
28387             }
28388         }
28389         this.stripEl.dom.removeChild(tab.pnode.dom);
28390         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28391             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28392         }
28393         items.splice(index, 1);
28394         delete this.items[tab.id];
28395         tab.fireEvent("close", tab);
28396         tab.purgeListeners();
28397         this.autoSizeTabs();
28398     },
28399
28400     getNextAvailable : function(start){
28401         var items = this.items;
28402         var index = start;
28403         // look for a next tab that will slide over to
28404         // replace the one being removed
28405         while(index < items.length){
28406             var item = items[++index];
28407             if(item && !item.isHidden()){
28408                 return item;
28409             }
28410         }
28411         // if one isn't found select the previous tab (on the left)
28412         index = start;
28413         while(index >= 0){
28414             var item = items[--index];
28415             if(item && !item.isHidden()){
28416                 return item;
28417             }
28418         }
28419         return null;
28420     },
28421
28422     /**
28423      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28424      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28425      */
28426     disableTab : function(id){
28427         var tab = this.items[id];
28428         if(tab && this.active != tab){
28429             tab.disable();
28430         }
28431     },
28432
28433     /**
28434      * Enables a {@link Roo.TabPanelItem} that is disabled.
28435      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28436      */
28437     enableTab : function(id){
28438         var tab = this.items[id];
28439         tab.enable();
28440     },
28441
28442     /**
28443      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28444      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28445      * @return {Roo.TabPanelItem} The TabPanelItem.
28446      */
28447     activate : function(id){
28448         var tab = this.items[id];
28449         if(!tab){
28450             return null;
28451         }
28452         if(tab == this.active || tab.disabled){
28453             return tab;
28454         }
28455         var e = {};
28456         this.fireEvent("beforetabchange", this, e, tab);
28457         if(e.cancel !== true && !tab.disabled){
28458             if(this.active){
28459                 this.active.hide();
28460             }
28461             this.active = this.items[id];
28462             this.active.show();
28463             this.fireEvent("tabchange", this, this.active);
28464         }
28465         return tab;
28466     },
28467
28468     /**
28469      * Gets the active {@link Roo.TabPanelItem}.
28470      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28471      */
28472     getActiveTab : function(){
28473         return this.active;
28474     },
28475
28476     /**
28477      * Updates the tab body element to fit the height of the container element
28478      * for overflow scrolling
28479      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28480      */
28481     syncHeight : function(targetHeight){
28482         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28483         var bm = this.bodyEl.getMargins();
28484         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28485         this.bodyEl.setHeight(newHeight);
28486         return newHeight;
28487     },
28488
28489     onResize : function(){
28490         if(this.monitorResize){
28491             this.autoSizeTabs();
28492         }
28493     },
28494
28495     /**
28496      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28497      */
28498     beginUpdate : function(){
28499         this.updating = true;
28500     },
28501
28502     /**
28503      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28504      */
28505     endUpdate : function(){
28506         this.updating = false;
28507         this.autoSizeTabs();
28508     },
28509
28510     /**
28511      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28512      */
28513     autoSizeTabs : function(){
28514         var count = this.items.length;
28515         var vcount = count - this.hiddenCount;
28516         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28517             return;
28518         }
28519         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28520         var availWidth = Math.floor(w / vcount);
28521         var b = this.stripBody;
28522         if(b.getWidth() > w){
28523             var tabs = this.items;
28524             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28525             if(availWidth < this.minTabWidth){
28526                 /*if(!this.sleft){    // incomplete scrolling code
28527                     this.createScrollButtons();
28528                 }
28529                 this.showScroll();
28530                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28531             }
28532         }else{
28533             if(this.currentTabWidth < this.preferredTabWidth){
28534                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28535             }
28536         }
28537     },
28538
28539     /**
28540      * Returns the number of tabs in this TabPanel.
28541      * @return {Number}
28542      */
28543      getCount : function(){
28544          return this.items.length;
28545      },
28546
28547     /**
28548      * Resizes all the tabs to the passed width
28549      * @param {Number} The new width
28550      */
28551     setTabWidth : function(width){
28552         this.currentTabWidth = width;
28553         for(var i = 0, len = this.items.length; i < len; i++) {
28554                 if(!this.items[i].isHidden()) {
28555                 this.items[i].setWidth(width);
28556             }
28557         }
28558     },
28559
28560     /**
28561      * Destroys this TabPanel
28562      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28563      */
28564     destroy : function(removeEl){
28565         Roo.EventManager.removeResizeListener(this.onResize, this);
28566         for(var i = 0, len = this.items.length; i < len; i++){
28567             this.items[i].purgeListeners();
28568         }
28569         if(removeEl === true){
28570             this.el.update("");
28571             this.el.remove();
28572         }
28573     }
28574 });
28575
28576 /**
28577  * @class Roo.TabPanelItem
28578  * @extends Roo.util.Observable
28579  * Represents an individual item (tab plus body) in a TabPanel.
28580  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28581  * @param {String} id The id of this TabPanelItem
28582  * @param {String} text The text for the tab of this TabPanelItem
28583  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28584  */
28585 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28586     /**
28587      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28588      * @type Roo.TabPanel
28589      */
28590     this.tabPanel = tabPanel;
28591     /**
28592      * The id for this TabPanelItem
28593      * @type String
28594      */
28595     this.id = id;
28596     /** @private */
28597     this.disabled = false;
28598     /** @private */
28599     this.text = text;
28600     /** @private */
28601     this.loaded = false;
28602     this.closable = closable;
28603
28604     /**
28605      * The body element for this TabPanelItem.
28606      * @type Roo.Element
28607      */
28608     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28609     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28610     this.bodyEl.setStyle("display", "block");
28611     this.bodyEl.setStyle("zoom", "1");
28612     this.hideAction();
28613
28614     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28615     /** @private */
28616     this.el = Roo.get(els.el, true);
28617     this.inner = Roo.get(els.inner, true);
28618     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28619     this.pnode = Roo.get(els.el.parentNode, true);
28620     this.el.on("mousedown", this.onTabMouseDown, this);
28621     this.el.on("click", this.onTabClick, this);
28622     /** @private */
28623     if(closable){
28624         var c = Roo.get(els.close, true);
28625         c.dom.title = this.closeText;
28626         c.addClassOnOver("close-over");
28627         c.on("click", this.closeClick, this);
28628      }
28629
28630     this.addEvents({
28631          /**
28632          * @event activate
28633          * Fires when this tab becomes the active tab.
28634          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28635          * @param {Roo.TabPanelItem} this
28636          */
28637         "activate": true,
28638         /**
28639          * @event beforeclose
28640          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28641          * @param {Roo.TabPanelItem} this
28642          * @param {Object} e Set cancel to true on this object to cancel the close.
28643          */
28644         "beforeclose": true,
28645         /**
28646          * @event close
28647          * Fires when this tab is closed.
28648          * @param {Roo.TabPanelItem} this
28649          */
28650          "close": true,
28651         /**
28652          * @event deactivate
28653          * Fires when this tab is no longer the active tab.
28654          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28655          * @param {Roo.TabPanelItem} this
28656          */
28657          "deactivate" : true
28658     });
28659     this.hidden = false;
28660
28661     Roo.TabPanelItem.superclass.constructor.call(this);
28662 };
28663
28664 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28665     purgeListeners : function(){
28666        Roo.util.Observable.prototype.purgeListeners.call(this);
28667        this.el.removeAllListeners();
28668     },
28669     /**
28670      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28671      */
28672     show : function(){
28673         this.pnode.addClass("on");
28674         this.showAction();
28675         if(Roo.isOpera){
28676             this.tabPanel.stripWrap.repaint();
28677         }
28678         this.fireEvent("activate", this.tabPanel, this);
28679     },
28680
28681     /**
28682      * Returns true if this tab is the active tab.
28683      * @return {Boolean}
28684      */
28685     isActive : function(){
28686         return this.tabPanel.getActiveTab() == this;
28687     },
28688
28689     /**
28690      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28691      */
28692     hide : function(){
28693         this.pnode.removeClass("on");
28694         this.hideAction();
28695         this.fireEvent("deactivate", this.tabPanel, this);
28696     },
28697
28698     hideAction : function(){
28699         this.bodyEl.hide();
28700         this.bodyEl.setStyle("position", "absolute");
28701         this.bodyEl.setLeft("-20000px");
28702         this.bodyEl.setTop("-20000px");
28703     },
28704
28705     showAction : function(){
28706         this.bodyEl.setStyle("position", "relative");
28707         this.bodyEl.setTop("");
28708         this.bodyEl.setLeft("");
28709         this.bodyEl.show();
28710     },
28711
28712     /**
28713      * Set the tooltip for the tab.
28714      * @param {String} tooltip The tab's tooltip
28715      */
28716     setTooltip : function(text){
28717         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28718             this.textEl.dom.qtip = text;
28719             this.textEl.dom.removeAttribute('title');
28720         }else{
28721             this.textEl.dom.title = text;
28722         }
28723     },
28724
28725     onTabClick : function(e){
28726         e.preventDefault();
28727         this.tabPanel.activate(this.id);
28728     },
28729
28730     onTabMouseDown : function(e){
28731         e.preventDefault();
28732         this.tabPanel.activate(this.id);
28733     },
28734
28735     getWidth : function(){
28736         return this.inner.getWidth();
28737     },
28738
28739     setWidth : function(width){
28740         var iwidth = width - this.pnode.getPadding("lr");
28741         this.inner.setWidth(iwidth);
28742         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28743         this.pnode.setWidth(width);
28744     },
28745
28746     /**
28747      * Show or hide the tab
28748      * @param {Boolean} hidden True to hide or false to show.
28749      */
28750     setHidden : function(hidden){
28751         this.hidden = hidden;
28752         this.pnode.setStyle("display", hidden ? "none" : "");
28753     },
28754
28755     /**
28756      * Returns true if this tab is "hidden"
28757      * @return {Boolean}
28758      */
28759     isHidden : function(){
28760         return this.hidden;
28761     },
28762
28763     /**
28764      * Returns the text for this tab
28765      * @return {String}
28766      */
28767     getText : function(){
28768         return this.text;
28769     },
28770
28771     autoSize : function(){
28772         //this.el.beginMeasure();
28773         this.textEl.setWidth(1);
28774         /*
28775          *  #2804 [new] Tabs in Roojs
28776          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28777          */
28778         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28779         //this.el.endMeasure();
28780     },
28781
28782     /**
28783      * Sets the text for the tab (Note: this also sets the tooltip text)
28784      * @param {String} text The tab's text and tooltip
28785      */
28786     setText : function(text){
28787         this.text = text;
28788         this.textEl.update(text);
28789         this.setTooltip(text);
28790         if(!this.tabPanel.resizeTabs){
28791             this.autoSize();
28792         }
28793     },
28794     /**
28795      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28796      */
28797     activate : function(){
28798         this.tabPanel.activate(this.id);
28799     },
28800
28801     /**
28802      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28803      */
28804     disable : function(){
28805         if(this.tabPanel.active != this){
28806             this.disabled = true;
28807             this.pnode.addClass("disabled");
28808         }
28809     },
28810
28811     /**
28812      * Enables this TabPanelItem if it was previously disabled.
28813      */
28814     enable : function(){
28815         this.disabled = false;
28816         this.pnode.removeClass("disabled");
28817     },
28818
28819     /**
28820      * Sets the content for this TabPanelItem.
28821      * @param {String} content The content
28822      * @param {Boolean} loadScripts true to look for and load scripts
28823      */
28824     setContent : function(content, loadScripts){
28825         this.bodyEl.update(content, loadScripts);
28826     },
28827
28828     /**
28829      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28830      * @return {Roo.UpdateManager} The UpdateManager
28831      */
28832     getUpdateManager : function(){
28833         return this.bodyEl.getUpdateManager();
28834     },
28835
28836     /**
28837      * Set a URL to be used to load the content for this TabPanelItem.
28838      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28839      * @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)
28840      * @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)
28841      * @return {Roo.UpdateManager} The UpdateManager
28842      */
28843     setUrl : function(url, params, loadOnce){
28844         if(this.refreshDelegate){
28845             this.un('activate', this.refreshDelegate);
28846         }
28847         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28848         this.on("activate", this.refreshDelegate);
28849         return this.bodyEl.getUpdateManager();
28850     },
28851
28852     /** @private */
28853     _handleRefresh : function(url, params, loadOnce){
28854         if(!loadOnce || !this.loaded){
28855             var updater = this.bodyEl.getUpdateManager();
28856             updater.update(url, params, this._setLoaded.createDelegate(this));
28857         }
28858     },
28859
28860     /**
28861      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28862      *   Will fail silently if the setUrl method has not been called.
28863      *   This does not activate the panel, just updates its content.
28864      */
28865     refresh : function(){
28866         if(this.refreshDelegate){
28867            this.loaded = false;
28868            this.refreshDelegate();
28869         }
28870     },
28871
28872     /** @private */
28873     _setLoaded : function(){
28874         this.loaded = true;
28875     },
28876
28877     /** @private */
28878     closeClick : function(e){
28879         var o = {};
28880         e.stopEvent();
28881         this.fireEvent("beforeclose", this, o);
28882         if(o.cancel !== true){
28883             this.tabPanel.removeTab(this.id);
28884         }
28885     },
28886     /**
28887      * The text displayed in the tooltip for the close icon.
28888      * @type String
28889      */
28890     closeText : "Close this tab"
28891 });
28892
28893 /** @private */
28894 Roo.TabPanel.prototype.createStrip = function(container){
28895     var strip = document.createElement("div");
28896     strip.className = "x-tabs-wrap";
28897     container.appendChild(strip);
28898     return strip;
28899 };
28900 /** @private */
28901 Roo.TabPanel.prototype.createStripList = function(strip){
28902     // div wrapper for retard IE
28903     // returns the "tr" element.
28904     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28905         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28906         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28907     return strip.firstChild.firstChild.firstChild.firstChild;
28908 };
28909 /** @private */
28910 Roo.TabPanel.prototype.createBody = function(container){
28911     var body = document.createElement("div");
28912     Roo.id(body, "tab-body");
28913     Roo.fly(body).addClass("x-tabs-body");
28914     container.appendChild(body);
28915     return body;
28916 };
28917 /** @private */
28918 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28919     var body = Roo.getDom(id);
28920     if(!body){
28921         body = document.createElement("div");
28922         body.id = id;
28923     }
28924     Roo.fly(body).addClass("x-tabs-item-body");
28925     bodyEl.insertBefore(body, bodyEl.firstChild);
28926     return body;
28927 };
28928 /** @private */
28929 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28930     var td = document.createElement("td");
28931     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28932     //stripEl.appendChild(td);
28933     if(closable){
28934         td.className = "x-tabs-closable";
28935         if(!this.closeTpl){
28936             this.closeTpl = new Roo.Template(
28937                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28938                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28939                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28940             );
28941         }
28942         var el = this.closeTpl.overwrite(td, {"text": text});
28943         var close = el.getElementsByTagName("div")[0];
28944         var inner = el.getElementsByTagName("em")[0];
28945         return {"el": el, "close": close, "inner": inner};
28946     } else {
28947         if(!this.tabTpl){
28948             this.tabTpl = new Roo.Template(
28949                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28950                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28951             );
28952         }
28953         var el = this.tabTpl.overwrite(td, {"text": text});
28954         var inner = el.getElementsByTagName("em")[0];
28955         return {"el": el, "inner": inner};
28956     }
28957 };/*
28958  * Based on:
28959  * Ext JS Library 1.1.1
28960  * Copyright(c) 2006-2007, Ext JS, LLC.
28961  *
28962  * Originally Released Under LGPL - original licence link has changed is not relivant.
28963  *
28964  * Fork - LGPL
28965  * <script type="text/javascript">
28966  */
28967
28968 /**
28969  * @class Roo.Button
28970  * @extends Roo.util.Observable
28971  * Simple Button class
28972  * @cfg {String} text The button text
28973  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
28974  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
28975  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
28976  * @cfg {Object} scope The scope of the handler
28977  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
28978  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
28979  * @cfg {Boolean} hidden True to start hidden (defaults to false)
28980  * @cfg {Boolean} disabled True to start disabled (defaults to false)
28981  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
28982  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
28983    applies if enableToggle = true)
28984  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
28985  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
28986   an {@link Roo.util.ClickRepeater} config object (defaults to false).
28987  * @constructor
28988  * Create a new button
28989  * @param {Object} config The config object
28990  */
28991 Roo.Button = function(renderTo, config)
28992 {
28993     if (!config) {
28994         config = renderTo;
28995         renderTo = config.renderTo || false;
28996     }
28997     
28998     Roo.apply(this, config);
28999     this.addEvents({
29000         /**
29001              * @event click
29002              * Fires when this button is clicked
29003              * @param {Button} this
29004              * @param {EventObject} e The click event
29005              */
29006             "click" : true,
29007         /**
29008              * @event toggle
29009              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29010              * @param {Button} this
29011              * @param {Boolean} pressed
29012              */
29013             "toggle" : true,
29014         /**
29015              * @event mouseover
29016              * Fires when the mouse hovers over the button
29017              * @param {Button} this
29018              * @param {Event} e The event object
29019              */
29020         'mouseover' : true,
29021         /**
29022              * @event mouseout
29023              * Fires when the mouse exits the button
29024              * @param {Button} this
29025              * @param {Event} e The event object
29026              */
29027         'mouseout': true,
29028          /**
29029              * @event render
29030              * Fires when the button is rendered
29031              * @param {Button} this
29032              */
29033         'render': true
29034     });
29035     if(this.menu){
29036         this.menu = Roo.menu.MenuMgr.get(this.menu);
29037     }
29038     // register listeners first!!  - so render can be captured..
29039     Roo.util.Observable.call(this);
29040     if(renderTo){
29041         this.render(renderTo);
29042     }
29043     
29044   
29045 };
29046
29047 Roo.extend(Roo.Button, Roo.util.Observable, {
29048     /**
29049      * 
29050      */
29051     
29052     /**
29053      * Read-only. True if this button is hidden
29054      * @type Boolean
29055      */
29056     hidden : false,
29057     /**
29058      * Read-only. True if this button is disabled
29059      * @type Boolean
29060      */
29061     disabled : false,
29062     /**
29063      * Read-only. True if this button is pressed (only if enableToggle = true)
29064      * @type Boolean
29065      */
29066     pressed : false,
29067
29068     /**
29069      * @cfg {Number} tabIndex 
29070      * The DOM tabIndex for this button (defaults to undefined)
29071      */
29072     tabIndex : undefined,
29073
29074     /**
29075      * @cfg {Boolean} enableToggle
29076      * True to enable pressed/not pressed toggling (defaults to false)
29077      */
29078     enableToggle: false,
29079     /**
29080      * @cfg {Mixed} menu
29081      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29082      */
29083     menu : undefined,
29084     /**
29085      * @cfg {String} menuAlign
29086      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29087      */
29088     menuAlign : "tl-bl?",
29089
29090     /**
29091      * @cfg {String} iconCls
29092      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29093      */
29094     iconCls : undefined,
29095     /**
29096      * @cfg {String} type
29097      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29098      */
29099     type : 'button',
29100
29101     // private
29102     menuClassTarget: 'tr',
29103
29104     /**
29105      * @cfg {String} clickEvent
29106      * The type of event to map to the button's event handler (defaults to 'click')
29107      */
29108     clickEvent : 'click',
29109
29110     /**
29111      * @cfg {Boolean} handleMouseEvents
29112      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29113      */
29114     handleMouseEvents : true,
29115
29116     /**
29117      * @cfg {String} tooltipType
29118      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29119      */
29120     tooltipType : 'qtip',
29121
29122     /**
29123      * @cfg {String} cls
29124      * A CSS class to apply to the button's main element.
29125      */
29126     
29127     /**
29128      * @cfg {Roo.Template} template (Optional)
29129      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29130      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29131      * require code modifications if required elements (e.g. a button) aren't present.
29132      */
29133
29134     // private
29135     render : function(renderTo){
29136         var btn;
29137         if(this.hideParent){
29138             this.parentEl = Roo.get(renderTo);
29139         }
29140         if(!this.dhconfig){
29141             if(!this.template){
29142                 if(!Roo.Button.buttonTemplate){
29143                     // hideous table template
29144                     Roo.Button.buttonTemplate = new Roo.Template(
29145                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29146                         '<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>',
29147                         "</tr></tbody></table>");
29148                 }
29149                 this.template = Roo.Button.buttonTemplate;
29150             }
29151             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29152             var btnEl = btn.child("button:first");
29153             btnEl.on('focus', this.onFocus, this);
29154             btnEl.on('blur', this.onBlur, this);
29155             if(this.cls){
29156                 btn.addClass(this.cls);
29157             }
29158             if(this.icon){
29159                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29160             }
29161             if(this.iconCls){
29162                 btnEl.addClass(this.iconCls);
29163                 if(!this.cls){
29164                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29165                 }
29166             }
29167             if(this.tabIndex !== undefined){
29168                 btnEl.dom.tabIndex = this.tabIndex;
29169             }
29170             if(this.tooltip){
29171                 if(typeof this.tooltip == 'object'){
29172                     Roo.QuickTips.tips(Roo.apply({
29173                           target: btnEl.id
29174                     }, this.tooltip));
29175                 } else {
29176                     btnEl.dom[this.tooltipType] = this.tooltip;
29177                 }
29178             }
29179         }else{
29180             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29181         }
29182         this.el = btn;
29183         if(this.id){
29184             this.el.dom.id = this.el.id = this.id;
29185         }
29186         if(this.menu){
29187             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29188             this.menu.on("show", this.onMenuShow, this);
29189             this.menu.on("hide", this.onMenuHide, this);
29190         }
29191         btn.addClass("x-btn");
29192         if(Roo.isIE && !Roo.isIE7){
29193             this.autoWidth.defer(1, this);
29194         }else{
29195             this.autoWidth();
29196         }
29197         if(this.handleMouseEvents){
29198             btn.on("mouseover", this.onMouseOver, this);
29199             btn.on("mouseout", this.onMouseOut, this);
29200             btn.on("mousedown", this.onMouseDown, this);
29201         }
29202         btn.on(this.clickEvent, this.onClick, this);
29203         //btn.on("mouseup", this.onMouseUp, this);
29204         if(this.hidden){
29205             this.hide();
29206         }
29207         if(this.disabled){
29208             this.disable();
29209         }
29210         Roo.ButtonToggleMgr.register(this);
29211         if(this.pressed){
29212             this.el.addClass("x-btn-pressed");
29213         }
29214         if(this.repeat){
29215             var repeater = new Roo.util.ClickRepeater(btn,
29216                 typeof this.repeat == "object" ? this.repeat : {}
29217             );
29218             repeater.on("click", this.onClick,  this);
29219         }
29220         
29221         this.fireEvent('render', this);
29222         
29223     },
29224     /**
29225      * Returns the button's underlying element
29226      * @return {Roo.Element} The element
29227      */
29228     getEl : function(){
29229         return this.el;  
29230     },
29231     
29232     /**
29233      * Destroys this Button and removes any listeners.
29234      */
29235     destroy : function(){
29236         Roo.ButtonToggleMgr.unregister(this);
29237         this.el.removeAllListeners();
29238         this.purgeListeners();
29239         this.el.remove();
29240     },
29241
29242     // private
29243     autoWidth : function(){
29244         if(this.el){
29245             this.el.setWidth("auto");
29246             if(Roo.isIE7 && Roo.isStrict){
29247                 var ib = this.el.child('button');
29248                 if(ib && ib.getWidth() > 20){
29249                     ib.clip();
29250                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29251                 }
29252             }
29253             if(this.minWidth){
29254                 if(this.hidden){
29255                     this.el.beginMeasure();
29256                 }
29257                 if(this.el.getWidth() < this.minWidth){
29258                     this.el.setWidth(this.minWidth);
29259                 }
29260                 if(this.hidden){
29261                     this.el.endMeasure();
29262                 }
29263             }
29264         }
29265     },
29266
29267     /**
29268      * Assigns this button's click handler
29269      * @param {Function} handler The function to call when the button is clicked
29270      * @param {Object} scope (optional) Scope for the function passed in
29271      */
29272     setHandler : function(handler, scope){
29273         this.handler = handler;
29274         this.scope = scope;  
29275     },
29276     
29277     /**
29278      * Sets this button's text
29279      * @param {String} text The button text
29280      */
29281     setText : function(text){
29282         this.text = text;
29283         if(this.el){
29284             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29285         }
29286         this.autoWidth();
29287     },
29288     
29289     /**
29290      * Gets the text for this button
29291      * @return {String} The button text
29292      */
29293     getText : function(){
29294         return this.text;  
29295     },
29296     
29297     /**
29298      * Show this button
29299      */
29300     show: function(){
29301         this.hidden = false;
29302         if(this.el){
29303             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29304         }
29305     },
29306     
29307     /**
29308      * Hide this button
29309      */
29310     hide: function(){
29311         this.hidden = true;
29312         if(this.el){
29313             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29314         }
29315     },
29316     
29317     /**
29318      * Convenience function for boolean show/hide
29319      * @param {Boolean} visible True to show, false to hide
29320      */
29321     setVisible: function(visible){
29322         if(visible) {
29323             this.show();
29324         }else{
29325             this.hide();
29326         }
29327     },
29328     
29329     /**
29330      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29331      * @param {Boolean} state (optional) Force a particular state
29332      */
29333     toggle : function(state){
29334         state = state === undefined ? !this.pressed : state;
29335         if(state != this.pressed){
29336             if(state){
29337                 this.el.addClass("x-btn-pressed");
29338                 this.pressed = true;
29339                 this.fireEvent("toggle", this, true);
29340             }else{
29341                 this.el.removeClass("x-btn-pressed");
29342                 this.pressed = false;
29343                 this.fireEvent("toggle", this, false);
29344             }
29345             if(this.toggleHandler){
29346                 this.toggleHandler.call(this.scope || this, this, state);
29347             }
29348         }
29349     },
29350     
29351     /**
29352      * Focus the button
29353      */
29354     focus : function(){
29355         this.el.child('button:first').focus();
29356     },
29357     
29358     /**
29359      * Disable this button
29360      */
29361     disable : function(){
29362         if(this.el){
29363             this.el.addClass("x-btn-disabled");
29364         }
29365         this.disabled = true;
29366     },
29367     
29368     /**
29369      * Enable this button
29370      */
29371     enable : function(){
29372         if(this.el){
29373             this.el.removeClass("x-btn-disabled");
29374         }
29375         this.disabled = false;
29376     },
29377
29378     /**
29379      * Convenience function for boolean enable/disable
29380      * @param {Boolean} enabled True to enable, false to disable
29381      */
29382     setDisabled : function(v){
29383         this[v !== true ? "enable" : "disable"]();
29384     },
29385
29386     // private
29387     onClick : function(e)
29388     {
29389         if(e){
29390             e.preventDefault();
29391         }
29392         if(e.button != 0){
29393             return;
29394         }
29395         if(!this.disabled){
29396             if(this.enableToggle){
29397                 this.toggle();
29398             }
29399             if(this.menu && !this.menu.isVisible()){
29400                 this.menu.show(this.el, this.menuAlign);
29401             }
29402             this.fireEvent("click", this, e);
29403             if(this.handler){
29404                 this.el.removeClass("x-btn-over");
29405                 this.handler.call(this.scope || this, this, e);
29406             }
29407         }
29408     },
29409     // private
29410     onMouseOver : function(e){
29411         if(!this.disabled){
29412             this.el.addClass("x-btn-over");
29413             this.fireEvent('mouseover', this, e);
29414         }
29415     },
29416     // private
29417     onMouseOut : function(e){
29418         if(!e.within(this.el,  true)){
29419             this.el.removeClass("x-btn-over");
29420             this.fireEvent('mouseout', this, e);
29421         }
29422     },
29423     // private
29424     onFocus : function(e){
29425         if(!this.disabled){
29426             this.el.addClass("x-btn-focus");
29427         }
29428     },
29429     // private
29430     onBlur : function(e){
29431         this.el.removeClass("x-btn-focus");
29432     },
29433     // private
29434     onMouseDown : function(e){
29435         if(!this.disabled && e.button == 0){
29436             this.el.addClass("x-btn-click");
29437             Roo.get(document).on('mouseup', this.onMouseUp, this);
29438         }
29439     },
29440     // private
29441     onMouseUp : function(e){
29442         if(e.button == 0){
29443             this.el.removeClass("x-btn-click");
29444             Roo.get(document).un('mouseup', this.onMouseUp, this);
29445         }
29446     },
29447     // private
29448     onMenuShow : function(e){
29449         this.el.addClass("x-btn-menu-active");
29450     },
29451     // private
29452     onMenuHide : function(e){
29453         this.el.removeClass("x-btn-menu-active");
29454     }   
29455 });
29456
29457 // Private utility class used by Button
29458 Roo.ButtonToggleMgr = function(){
29459    var groups = {};
29460    
29461    function toggleGroup(btn, state){
29462        if(state){
29463            var g = groups[btn.toggleGroup];
29464            for(var i = 0, l = g.length; i < l; i++){
29465                if(g[i] != btn){
29466                    g[i].toggle(false);
29467                }
29468            }
29469        }
29470    }
29471    
29472    return {
29473        register : function(btn){
29474            if(!btn.toggleGroup){
29475                return;
29476            }
29477            var g = groups[btn.toggleGroup];
29478            if(!g){
29479                g = groups[btn.toggleGroup] = [];
29480            }
29481            g.push(btn);
29482            btn.on("toggle", toggleGroup);
29483        },
29484        
29485        unregister : function(btn){
29486            if(!btn.toggleGroup){
29487                return;
29488            }
29489            var g = groups[btn.toggleGroup];
29490            if(g){
29491                g.remove(btn);
29492                btn.un("toggle", toggleGroup);
29493            }
29494        }
29495    };
29496 }();/*
29497  * Based on:
29498  * Ext JS Library 1.1.1
29499  * Copyright(c) 2006-2007, Ext JS, LLC.
29500  *
29501  * Originally Released Under LGPL - original licence link has changed is not relivant.
29502  *
29503  * Fork - LGPL
29504  * <script type="text/javascript">
29505  */
29506  
29507 /**
29508  * @class Roo.SplitButton
29509  * @extends Roo.Button
29510  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29511  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29512  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29513  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29514  * @cfg {String} arrowTooltip The title attribute of the arrow
29515  * @constructor
29516  * Create a new menu button
29517  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29518  * @param {Object} config The config object
29519  */
29520 Roo.SplitButton = function(renderTo, config){
29521     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29522     /**
29523      * @event arrowclick
29524      * Fires when this button's arrow is clicked
29525      * @param {SplitButton} this
29526      * @param {EventObject} e The click event
29527      */
29528     this.addEvents({"arrowclick":true});
29529 };
29530
29531 Roo.extend(Roo.SplitButton, Roo.Button, {
29532     render : function(renderTo){
29533         // this is one sweet looking template!
29534         var tpl = new Roo.Template(
29535             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29536             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29537             '<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>',
29538             "</tbody></table></td><td>",
29539             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29540             '<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>',
29541             "</tbody></table></td></tr></table>"
29542         );
29543         var btn = tpl.append(renderTo, [this.text, this.type], true);
29544         var btnEl = btn.child("button");
29545         if(this.cls){
29546             btn.addClass(this.cls);
29547         }
29548         if(this.icon){
29549             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29550         }
29551         if(this.iconCls){
29552             btnEl.addClass(this.iconCls);
29553             if(!this.cls){
29554                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29555             }
29556         }
29557         this.el = btn;
29558         if(this.handleMouseEvents){
29559             btn.on("mouseover", this.onMouseOver, this);
29560             btn.on("mouseout", this.onMouseOut, this);
29561             btn.on("mousedown", this.onMouseDown, this);
29562             btn.on("mouseup", this.onMouseUp, this);
29563         }
29564         btn.on(this.clickEvent, this.onClick, this);
29565         if(this.tooltip){
29566             if(typeof this.tooltip == 'object'){
29567                 Roo.QuickTips.tips(Roo.apply({
29568                       target: btnEl.id
29569                 }, this.tooltip));
29570             } else {
29571                 btnEl.dom[this.tooltipType] = this.tooltip;
29572             }
29573         }
29574         if(this.arrowTooltip){
29575             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29576         }
29577         if(this.hidden){
29578             this.hide();
29579         }
29580         if(this.disabled){
29581             this.disable();
29582         }
29583         if(this.pressed){
29584             this.el.addClass("x-btn-pressed");
29585         }
29586         if(Roo.isIE && !Roo.isIE7){
29587             this.autoWidth.defer(1, this);
29588         }else{
29589             this.autoWidth();
29590         }
29591         if(this.menu){
29592             this.menu.on("show", this.onMenuShow, this);
29593             this.menu.on("hide", this.onMenuHide, this);
29594         }
29595         this.fireEvent('render', this);
29596     },
29597
29598     // private
29599     autoWidth : function(){
29600         if(this.el){
29601             var tbl = this.el.child("table:first");
29602             var tbl2 = this.el.child("table:last");
29603             this.el.setWidth("auto");
29604             tbl.setWidth("auto");
29605             if(Roo.isIE7 && Roo.isStrict){
29606                 var ib = this.el.child('button:first');
29607                 if(ib && ib.getWidth() > 20){
29608                     ib.clip();
29609                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29610                 }
29611             }
29612             if(this.minWidth){
29613                 if(this.hidden){
29614                     this.el.beginMeasure();
29615                 }
29616                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29617                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29618                 }
29619                 if(this.hidden){
29620                     this.el.endMeasure();
29621                 }
29622             }
29623             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29624         } 
29625     },
29626     /**
29627      * Sets this button's click handler
29628      * @param {Function} handler The function to call when the button is clicked
29629      * @param {Object} scope (optional) Scope for the function passed above
29630      */
29631     setHandler : function(handler, scope){
29632         this.handler = handler;
29633         this.scope = scope;  
29634     },
29635     
29636     /**
29637      * Sets this button's arrow click handler
29638      * @param {Function} handler The function to call when the arrow is clicked
29639      * @param {Object} scope (optional) Scope for the function passed above
29640      */
29641     setArrowHandler : function(handler, scope){
29642         this.arrowHandler = handler;
29643         this.scope = scope;  
29644     },
29645     
29646     /**
29647      * Focus the button
29648      */
29649     focus : function(){
29650         if(this.el){
29651             this.el.child("button:first").focus();
29652         }
29653     },
29654
29655     // private
29656     onClick : function(e){
29657         e.preventDefault();
29658         if(!this.disabled){
29659             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29660                 if(this.menu && !this.menu.isVisible()){
29661                     this.menu.show(this.el, this.menuAlign);
29662                 }
29663                 this.fireEvent("arrowclick", this, e);
29664                 if(this.arrowHandler){
29665                     this.arrowHandler.call(this.scope || this, this, e);
29666                 }
29667             }else{
29668                 this.fireEvent("click", this, e);
29669                 if(this.handler){
29670                     this.handler.call(this.scope || this, this, e);
29671                 }
29672             }
29673         }
29674     },
29675     // private
29676     onMouseDown : function(e){
29677         if(!this.disabled){
29678             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29679         }
29680     },
29681     // private
29682     onMouseUp : function(e){
29683         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29684     }   
29685 });
29686
29687
29688 // backwards compat
29689 Roo.MenuButton = Roo.SplitButton;/*
29690  * Based on:
29691  * Ext JS Library 1.1.1
29692  * Copyright(c) 2006-2007, Ext JS, LLC.
29693  *
29694  * Originally Released Under LGPL - original licence link has changed is not relivant.
29695  *
29696  * Fork - LGPL
29697  * <script type="text/javascript">
29698  */
29699
29700 /**
29701  * @class Roo.Toolbar
29702  * Basic Toolbar class.
29703  * @constructor
29704  * Creates a new Toolbar
29705  * @param {Object} container The config object
29706  */ 
29707 Roo.Toolbar = function(container, buttons, config)
29708 {
29709     /// old consturctor format still supported..
29710     if(container instanceof Array){ // omit the container for later rendering
29711         buttons = container;
29712         config = buttons;
29713         container = null;
29714     }
29715     if (typeof(container) == 'object' && container.xtype) {
29716         config = container;
29717         container = config.container;
29718         buttons = config.buttons || []; // not really - use items!!
29719     }
29720     var xitems = [];
29721     if (config && config.items) {
29722         xitems = config.items;
29723         delete config.items;
29724     }
29725     Roo.apply(this, config);
29726     this.buttons = buttons;
29727     
29728     if(container){
29729         this.render(container);
29730     }
29731     this.xitems = xitems;
29732     Roo.each(xitems, function(b) {
29733         this.add(b);
29734     }, this);
29735     
29736 };
29737
29738 Roo.Toolbar.prototype = {
29739     /**
29740      * @cfg {Array} items
29741      * array of button configs or elements to add (will be converted to a MixedCollection)
29742      */
29743     
29744     /**
29745      * @cfg {String/HTMLElement/Element} container
29746      * The id or element that will contain the toolbar
29747      */
29748     // private
29749     render : function(ct){
29750         this.el = Roo.get(ct);
29751         if(this.cls){
29752             this.el.addClass(this.cls);
29753         }
29754         // using a table allows for vertical alignment
29755         // 100% width is needed by Safari...
29756         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29757         this.tr = this.el.child("tr", true);
29758         var autoId = 0;
29759         this.items = new Roo.util.MixedCollection(false, function(o){
29760             return o.id || ("item" + (++autoId));
29761         });
29762         if(this.buttons){
29763             this.add.apply(this, this.buttons);
29764             delete this.buttons;
29765         }
29766     },
29767
29768     /**
29769      * Adds element(s) to the toolbar -- this function takes a variable number of 
29770      * arguments of mixed type and adds them to the toolbar.
29771      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29772      * <ul>
29773      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29774      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29775      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29776      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29777      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29778      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29779      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29780      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29781      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29782      * </ul>
29783      * @param {Mixed} arg2
29784      * @param {Mixed} etc.
29785      */
29786     add : function(){
29787         var a = arguments, l = a.length;
29788         for(var i = 0; i < l; i++){
29789             this._add(a[i]);
29790         }
29791     },
29792     // private..
29793     _add : function(el) {
29794         
29795         if (el.xtype) {
29796             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29797         }
29798         
29799         if (el.applyTo){ // some kind of form field
29800             return this.addField(el);
29801         } 
29802         if (el.render){ // some kind of Toolbar.Item
29803             return this.addItem(el);
29804         }
29805         if (typeof el == "string"){ // string
29806             if(el == "separator" || el == "-"){
29807                 return this.addSeparator();
29808             }
29809             if (el == " "){
29810                 return this.addSpacer();
29811             }
29812             if(el == "->"){
29813                 return this.addFill();
29814             }
29815             return this.addText(el);
29816             
29817         }
29818         if(el.tagName){ // element
29819             return this.addElement(el);
29820         }
29821         if(typeof el == "object"){ // must be button config?
29822             return this.addButton(el);
29823         }
29824         // and now what?!?!
29825         return false;
29826         
29827     },
29828     
29829     /**
29830      * Add an Xtype element
29831      * @param {Object} xtype Xtype Object
29832      * @return {Object} created Object
29833      */
29834     addxtype : function(e){
29835         return this.add(e);  
29836     },
29837     
29838     /**
29839      * Returns the Element for this toolbar.
29840      * @return {Roo.Element}
29841      */
29842     getEl : function(){
29843         return this.el;  
29844     },
29845     
29846     /**
29847      * Adds a separator
29848      * @return {Roo.Toolbar.Item} The separator item
29849      */
29850     addSeparator : function(){
29851         return this.addItem(new Roo.Toolbar.Separator());
29852     },
29853
29854     /**
29855      * Adds a spacer element
29856      * @return {Roo.Toolbar.Spacer} The spacer item
29857      */
29858     addSpacer : function(){
29859         return this.addItem(new Roo.Toolbar.Spacer());
29860     },
29861
29862     /**
29863      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29864      * @return {Roo.Toolbar.Fill} The fill item
29865      */
29866     addFill : function(){
29867         return this.addItem(new Roo.Toolbar.Fill());
29868     },
29869
29870     /**
29871      * Adds any standard HTML element to the toolbar
29872      * @param {String/HTMLElement/Element} el The element or id of the element to add
29873      * @return {Roo.Toolbar.Item} The element's item
29874      */
29875     addElement : function(el){
29876         return this.addItem(new Roo.Toolbar.Item(el));
29877     },
29878     /**
29879      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29880      * @type Roo.util.MixedCollection  
29881      */
29882     items : false,
29883      
29884     /**
29885      * Adds any Toolbar.Item or subclass
29886      * @param {Roo.Toolbar.Item} item
29887      * @return {Roo.Toolbar.Item} The item
29888      */
29889     addItem : function(item){
29890         var td = this.nextBlock();
29891         item.render(td);
29892         this.items.add(item);
29893         return item;
29894     },
29895     
29896     /**
29897      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29898      * @param {Object/Array} config A button config or array of configs
29899      * @return {Roo.Toolbar.Button/Array}
29900      */
29901     addButton : function(config){
29902         if(config instanceof Array){
29903             var buttons = [];
29904             for(var i = 0, len = config.length; i < len; i++) {
29905                 buttons.push(this.addButton(config[i]));
29906             }
29907             return buttons;
29908         }
29909         var b = config;
29910         if(!(config instanceof Roo.Toolbar.Button)){
29911             b = config.split ?
29912                 new Roo.Toolbar.SplitButton(config) :
29913                 new Roo.Toolbar.Button(config);
29914         }
29915         var td = this.nextBlock();
29916         b.render(td);
29917         this.items.add(b);
29918         return b;
29919     },
29920     
29921     /**
29922      * Adds text to the toolbar
29923      * @param {String} text The text to add
29924      * @return {Roo.Toolbar.Item} The element's item
29925      */
29926     addText : function(text){
29927         return this.addItem(new Roo.Toolbar.TextItem(text));
29928     },
29929     
29930     /**
29931      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29932      * @param {Number} index The index where the item is to be inserted
29933      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29934      * @return {Roo.Toolbar.Button/Item}
29935      */
29936     insertButton : function(index, item){
29937         if(item instanceof Array){
29938             var buttons = [];
29939             for(var i = 0, len = item.length; i < len; i++) {
29940                buttons.push(this.insertButton(index + i, item[i]));
29941             }
29942             return buttons;
29943         }
29944         if (!(item instanceof Roo.Toolbar.Button)){
29945            item = new Roo.Toolbar.Button(item);
29946         }
29947         var td = document.createElement("td");
29948         this.tr.insertBefore(td, this.tr.childNodes[index]);
29949         item.render(td);
29950         this.items.insert(index, item);
29951         return item;
29952     },
29953     
29954     /**
29955      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29956      * @param {Object} config
29957      * @return {Roo.Toolbar.Item} The element's item
29958      */
29959     addDom : function(config, returnEl){
29960         var td = this.nextBlock();
29961         Roo.DomHelper.overwrite(td, config);
29962         var ti = new Roo.Toolbar.Item(td.firstChild);
29963         ti.render(td);
29964         this.items.add(ti);
29965         return ti;
29966     },
29967
29968     /**
29969      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
29970      * @type Roo.util.MixedCollection  
29971      */
29972     fields : false,
29973     
29974     /**
29975      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
29976      * Note: the field should not have been rendered yet. For a field that has already been
29977      * rendered, use {@link #addElement}.
29978      * @param {Roo.form.Field} field
29979      * @return {Roo.ToolbarItem}
29980      */
29981      
29982       
29983     addField : function(field) {
29984         if (!this.fields) {
29985             var autoId = 0;
29986             this.fields = new Roo.util.MixedCollection(false, function(o){
29987                 return o.id || ("item" + (++autoId));
29988             });
29989
29990         }
29991         
29992         var td = this.nextBlock();
29993         field.render(td);
29994         var ti = new Roo.Toolbar.Item(td.firstChild);
29995         ti.render(td);
29996         this.items.add(ti);
29997         this.fields.add(field);
29998         return ti;
29999     },
30000     /**
30001      * Hide the toolbar
30002      * @method hide
30003      */
30004      
30005       
30006     hide : function()
30007     {
30008         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30009         this.el.child('div').hide();
30010     },
30011     /**
30012      * Show the toolbar
30013      * @method show
30014      */
30015     show : function()
30016     {
30017         this.el.child('div').show();
30018     },
30019       
30020     // private
30021     nextBlock : function(){
30022         var td = document.createElement("td");
30023         this.tr.appendChild(td);
30024         return td;
30025     },
30026
30027     // private
30028     destroy : function(){
30029         if(this.items){ // rendered?
30030             Roo.destroy.apply(Roo, this.items.items);
30031         }
30032         if(this.fields){ // rendered?
30033             Roo.destroy.apply(Roo, this.fields.items);
30034         }
30035         Roo.Element.uncache(this.el, this.tr);
30036     }
30037 };
30038
30039 /**
30040  * @class Roo.Toolbar.Item
30041  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30042  * @constructor
30043  * Creates a new Item
30044  * @param {HTMLElement} el 
30045  */
30046 Roo.Toolbar.Item = function(el){
30047     var cfg = {};
30048     if (typeof (el.xtype) != 'undefined') {
30049         cfg = el;
30050         el = cfg.el;
30051     }
30052     
30053     this.el = Roo.getDom(el);
30054     this.id = Roo.id(this.el);
30055     this.hidden = false;
30056     
30057     this.addEvents({
30058          /**
30059              * @event render
30060              * Fires when the button is rendered
30061              * @param {Button} this
30062              */
30063         'render': true
30064     });
30065     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30066 };
30067 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30068 //Roo.Toolbar.Item.prototype = {
30069     
30070     /**
30071      * Get this item's HTML Element
30072      * @return {HTMLElement}
30073      */
30074     getEl : function(){
30075        return this.el;  
30076     },
30077
30078     // private
30079     render : function(td){
30080         
30081          this.td = td;
30082         td.appendChild(this.el);
30083         
30084         this.fireEvent('render', this);
30085     },
30086     
30087     /**
30088      * Removes and destroys this item.
30089      */
30090     destroy : function(){
30091         this.td.parentNode.removeChild(this.td);
30092     },
30093     
30094     /**
30095      * Shows this item.
30096      */
30097     show: function(){
30098         this.hidden = false;
30099         this.td.style.display = "";
30100     },
30101     
30102     /**
30103      * Hides this item.
30104      */
30105     hide: function(){
30106         this.hidden = true;
30107         this.td.style.display = "none";
30108     },
30109     
30110     /**
30111      * Convenience function for boolean show/hide.
30112      * @param {Boolean} visible true to show/false to hide
30113      */
30114     setVisible: function(visible){
30115         if(visible) {
30116             this.show();
30117         }else{
30118             this.hide();
30119         }
30120     },
30121     
30122     /**
30123      * Try to focus this item.
30124      */
30125     focus : function(){
30126         Roo.fly(this.el).focus();
30127     },
30128     
30129     /**
30130      * Disables this item.
30131      */
30132     disable : function(){
30133         Roo.fly(this.td).addClass("x-item-disabled");
30134         this.disabled = true;
30135         this.el.disabled = true;
30136     },
30137     
30138     /**
30139      * Enables this item.
30140      */
30141     enable : function(){
30142         Roo.fly(this.td).removeClass("x-item-disabled");
30143         this.disabled = false;
30144         this.el.disabled = false;
30145     }
30146 });
30147
30148
30149 /**
30150  * @class Roo.Toolbar.Separator
30151  * @extends Roo.Toolbar.Item
30152  * A simple toolbar separator class
30153  * @constructor
30154  * Creates a new Separator
30155  */
30156 Roo.Toolbar.Separator = function(cfg){
30157     
30158     var s = document.createElement("span");
30159     s.className = "ytb-sep";
30160     if (cfg) {
30161         cfg.el = s;
30162     }
30163     
30164     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30165 };
30166 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30167     enable:Roo.emptyFn,
30168     disable:Roo.emptyFn,
30169     focus:Roo.emptyFn
30170 });
30171
30172 /**
30173  * @class Roo.Toolbar.Spacer
30174  * @extends Roo.Toolbar.Item
30175  * A simple element that adds extra horizontal space to a toolbar.
30176  * @constructor
30177  * Creates a new Spacer
30178  */
30179 Roo.Toolbar.Spacer = function(cfg){
30180     var s = document.createElement("div");
30181     s.className = "ytb-spacer";
30182     if (cfg) {
30183         cfg.el = s;
30184     }
30185     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30186 };
30187 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30188     enable:Roo.emptyFn,
30189     disable:Roo.emptyFn,
30190     focus:Roo.emptyFn
30191 });
30192
30193 /**
30194  * @class Roo.Toolbar.Fill
30195  * @extends Roo.Toolbar.Spacer
30196  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30197  * @constructor
30198  * Creates a new Spacer
30199  */
30200 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30201     // private
30202     render : function(td){
30203         td.style.width = '100%';
30204         Roo.Toolbar.Fill.superclass.render.call(this, td);
30205     }
30206 });
30207
30208 /**
30209  * @class Roo.Toolbar.TextItem
30210  * @extends Roo.Toolbar.Item
30211  * A simple class that renders text directly into a toolbar.
30212  * @constructor
30213  * Creates a new TextItem
30214  * @param {String} text
30215  */
30216 Roo.Toolbar.TextItem = function(cfg){
30217     var  text = cfg || "";
30218     if (typeof(cfg) == 'object') {
30219         text = cfg.text || "";
30220     }  else {
30221         cfg = null;
30222     }
30223     var s = document.createElement("span");
30224     s.className = "ytb-text";
30225     s.innerHTML = text;
30226     if (cfg) {
30227         cfg.el  = s;
30228     }
30229     
30230     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30231 };
30232 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30233     
30234      
30235     enable:Roo.emptyFn,
30236     disable:Roo.emptyFn,
30237     focus:Roo.emptyFn
30238 });
30239
30240 /**
30241  * @class Roo.Toolbar.Button
30242  * @extends Roo.Button
30243  * A button that renders into a toolbar.
30244  * @constructor
30245  * Creates a new Button
30246  * @param {Object} config A standard {@link Roo.Button} config object
30247  */
30248 Roo.Toolbar.Button = function(config){
30249     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30250 };
30251 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30252     render : function(td){
30253         this.td = td;
30254         Roo.Toolbar.Button.superclass.render.call(this, td);
30255     },
30256     
30257     /**
30258      * Removes and destroys this button
30259      */
30260     destroy : function(){
30261         Roo.Toolbar.Button.superclass.destroy.call(this);
30262         this.td.parentNode.removeChild(this.td);
30263     },
30264     
30265     /**
30266      * Shows this button
30267      */
30268     show: function(){
30269         this.hidden = false;
30270         this.td.style.display = "";
30271     },
30272     
30273     /**
30274      * Hides this button
30275      */
30276     hide: function(){
30277         this.hidden = true;
30278         this.td.style.display = "none";
30279     },
30280
30281     /**
30282      * Disables this item
30283      */
30284     disable : function(){
30285         Roo.fly(this.td).addClass("x-item-disabled");
30286         this.disabled = true;
30287     },
30288
30289     /**
30290      * Enables this item
30291      */
30292     enable : function(){
30293         Roo.fly(this.td).removeClass("x-item-disabled");
30294         this.disabled = false;
30295     }
30296 });
30297 // backwards compat
30298 Roo.ToolbarButton = Roo.Toolbar.Button;
30299
30300 /**
30301  * @class Roo.Toolbar.SplitButton
30302  * @extends Roo.SplitButton
30303  * A menu button that renders into a toolbar.
30304  * @constructor
30305  * Creates a new SplitButton
30306  * @param {Object} config A standard {@link Roo.SplitButton} config object
30307  */
30308 Roo.Toolbar.SplitButton = function(config){
30309     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30310 };
30311 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30312     render : function(td){
30313         this.td = td;
30314         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30315     },
30316     
30317     /**
30318      * Removes and destroys this button
30319      */
30320     destroy : function(){
30321         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30322         this.td.parentNode.removeChild(this.td);
30323     },
30324     
30325     /**
30326      * Shows this button
30327      */
30328     show: function(){
30329         this.hidden = false;
30330         this.td.style.display = "";
30331     },
30332     
30333     /**
30334      * Hides this button
30335      */
30336     hide: function(){
30337         this.hidden = true;
30338         this.td.style.display = "none";
30339     }
30340 });
30341
30342 // backwards compat
30343 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30344  * Based on:
30345  * Ext JS Library 1.1.1
30346  * Copyright(c) 2006-2007, Ext JS, LLC.
30347  *
30348  * Originally Released Under LGPL - original licence link has changed is not relivant.
30349  *
30350  * Fork - LGPL
30351  * <script type="text/javascript">
30352  */
30353  
30354 /**
30355  * @class Roo.PagingToolbar
30356  * @extends Roo.Toolbar
30357  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30358  * @constructor
30359  * Create a new PagingToolbar
30360  * @param {Object} config The config object
30361  */
30362 Roo.PagingToolbar = function(el, ds, config)
30363 {
30364     // old args format still supported... - xtype is prefered..
30365     if (typeof(el) == 'object' && el.xtype) {
30366         // created from xtype...
30367         config = el;
30368         ds = el.dataSource;
30369         el = config.container;
30370     }
30371     var items = [];
30372     if (config.items) {
30373         items = config.items;
30374         config.items = [];
30375     }
30376     
30377     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30378     this.ds = ds;
30379     this.cursor = 0;
30380     this.renderButtons(this.el);
30381     this.bind(ds);
30382     
30383     // supprot items array.
30384    
30385     Roo.each(items, function(e) {
30386         this.add(Roo.factory(e));
30387     },this);
30388     
30389 };
30390
30391 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30392     /**
30393      * @cfg {Roo.data.Store} dataSource
30394      * The underlying data store providing the paged data
30395      */
30396     /**
30397      * @cfg {String/HTMLElement/Element} container
30398      * container The id or element that will contain the toolbar
30399      */
30400     /**
30401      * @cfg {Boolean} displayInfo
30402      * True to display the displayMsg (defaults to false)
30403      */
30404     /**
30405      * @cfg {Number} pageSize
30406      * The number of records to display per page (defaults to 20)
30407      */
30408     pageSize: 20,
30409     /**
30410      * @cfg {String} displayMsg
30411      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30412      */
30413     displayMsg : 'Displaying {0} - {1} of {2}',
30414     /**
30415      * @cfg {String} emptyMsg
30416      * The message to display when no records are found (defaults to "No data to display")
30417      */
30418     emptyMsg : 'No data to display',
30419     /**
30420      * Customizable piece of the default paging text (defaults to "Page")
30421      * @type String
30422      */
30423     beforePageText : "Page",
30424     /**
30425      * Customizable piece of the default paging text (defaults to "of %0")
30426      * @type String
30427      */
30428     afterPageText : "of {0}",
30429     /**
30430      * Customizable piece of the default paging text (defaults to "First Page")
30431      * @type String
30432      */
30433     firstText : "First Page",
30434     /**
30435      * Customizable piece of the default paging text (defaults to "Previous Page")
30436      * @type String
30437      */
30438     prevText : "Previous Page",
30439     /**
30440      * Customizable piece of the default paging text (defaults to "Next Page")
30441      * @type String
30442      */
30443     nextText : "Next Page",
30444     /**
30445      * Customizable piece of the default paging text (defaults to "Last Page")
30446      * @type String
30447      */
30448     lastText : "Last Page",
30449     /**
30450      * Customizable piece of the default paging text (defaults to "Refresh")
30451      * @type String
30452      */
30453     refreshText : "Refresh",
30454
30455     // private
30456     renderButtons : function(el){
30457         Roo.PagingToolbar.superclass.render.call(this, el);
30458         this.first = this.addButton({
30459             tooltip: this.firstText,
30460             cls: "x-btn-icon x-grid-page-first",
30461             disabled: true,
30462             handler: this.onClick.createDelegate(this, ["first"])
30463         });
30464         this.prev = this.addButton({
30465             tooltip: this.prevText,
30466             cls: "x-btn-icon x-grid-page-prev",
30467             disabled: true,
30468             handler: this.onClick.createDelegate(this, ["prev"])
30469         });
30470         //this.addSeparator();
30471         this.add(this.beforePageText);
30472         this.field = Roo.get(this.addDom({
30473            tag: "input",
30474            type: "text",
30475            size: "3",
30476            value: "1",
30477            cls: "x-grid-page-number"
30478         }).el);
30479         this.field.on("keydown", this.onPagingKeydown, this);
30480         this.field.on("focus", function(){this.dom.select();});
30481         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30482         this.field.setHeight(18);
30483         //this.addSeparator();
30484         this.next = this.addButton({
30485             tooltip: this.nextText,
30486             cls: "x-btn-icon x-grid-page-next",
30487             disabled: true,
30488             handler: this.onClick.createDelegate(this, ["next"])
30489         });
30490         this.last = this.addButton({
30491             tooltip: this.lastText,
30492             cls: "x-btn-icon x-grid-page-last",
30493             disabled: true,
30494             handler: this.onClick.createDelegate(this, ["last"])
30495         });
30496         //this.addSeparator();
30497         this.loading = this.addButton({
30498             tooltip: this.refreshText,
30499             cls: "x-btn-icon x-grid-loading",
30500             handler: this.onClick.createDelegate(this, ["refresh"])
30501         });
30502
30503         if(this.displayInfo){
30504             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30505         }
30506     },
30507
30508     // private
30509     updateInfo : function(){
30510         if(this.displayEl){
30511             var count = this.ds.getCount();
30512             var msg = count == 0 ?
30513                 this.emptyMsg :
30514                 String.format(
30515                     this.displayMsg,
30516                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30517                 );
30518             this.displayEl.update(msg);
30519         }
30520     },
30521
30522     // private
30523     onLoad : function(ds, r, o){
30524        this.cursor = o.params ? o.params.start : 0;
30525        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30526
30527        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30528        this.field.dom.value = ap;
30529        this.first.setDisabled(ap == 1);
30530        this.prev.setDisabled(ap == 1);
30531        this.next.setDisabled(ap == ps);
30532        this.last.setDisabled(ap == ps);
30533        this.loading.enable();
30534        this.updateInfo();
30535     },
30536
30537     // private
30538     getPageData : function(){
30539         var total = this.ds.getTotalCount();
30540         return {
30541             total : total,
30542             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30543             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30544         };
30545     },
30546
30547     // private
30548     onLoadError : function(){
30549         this.loading.enable();
30550     },
30551
30552     // private
30553     onPagingKeydown : function(e){
30554         var k = e.getKey();
30555         var d = this.getPageData();
30556         if(k == e.RETURN){
30557             var v = this.field.dom.value, pageNum;
30558             if(!v || isNaN(pageNum = parseInt(v, 10))){
30559                 this.field.dom.value = d.activePage;
30560                 return;
30561             }
30562             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30563             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30564             e.stopEvent();
30565         }
30566         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))
30567         {
30568           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30569           this.field.dom.value = pageNum;
30570           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30571           e.stopEvent();
30572         }
30573         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30574         {
30575           var v = this.field.dom.value, pageNum; 
30576           var increment = (e.shiftKey) ? 10 : 1;
30577           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30578             increment *= -1;
30579           }
30580           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30581             this.field.dom.value = d.activePage;
30582             return;
30583           }
30584           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30585           {
30586             this.field.dom.value = parseInt(v, 10) + increment;
30587             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30588             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30589           }
30590           e.stopEvent();
30591         }
30592     },
30593
30594     // private
30595     beforeLoad : function(){
30596         if(this.loading){
30597             this.loading.disable();
30598         }
30599     },
30600
30601     // private
30602     onClick : function(which){
30603         var ds = this.ds;
30604         switch(which){
30605             case "first":
30606                 ds.load({params:{start: 0, limit: this.pageSize}});
30607             break;
30608             case "prev":
30609                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30610             break;
30611             case "next":
30612                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30613             break;
30614             case "last":
30615                 var total = ds.getTotalCount();
30616                 var extra = total % this.pageSize;
30617                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30618                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30619             break;
30620             case "refresh":
30621                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30622             break;
30623         }
30624     },
30625
30626     /**
30627      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30628      * @param {Roo.data.Store} store The data store to unbind
30629      */
30630     unbind : function(ds){
30631         ds.un("beforeload", this.beforeLoad, this);
30632         ds.un("load", this.onLoad, this);
30633         ds.un("loadexception", this.onLoadError, this);
30634         ds.un("remove", this.updateInfo, this);
30635         ds.un("add", this.updateInfo, this);
30636         this.ds = undefined;
30637     },
30638
30639     /**
30640      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30641      * @param {Roo.data.Store} store The data store to bind
30642      */
30643     bind : function(ds){
30644         ds.on("beforeload", this.beforeLoad, this);
30645         ds.on("load", this.onLoad, this);
30646         ds.on("loadexception", this.onLoadError, this);
30647         ds.on("remove", this.updateInfo, this);
30648         ds.on("add", this.updateInfo, this);
30649         this.ds = ds;
30650     }
30651 });/*
30652  * Based on:
30653  * Ext JS Library 1.1.1
30654  * Copyright(c) 2006-2007, Ext JS, LLC.
30655  *
30656  * Originally Released Under LGPL - original licence link has changed is not relivant.
30657  *
30658  * Fork - LGPL
30659  * <script type="text/javascript">
30660  */
30661
30662 /**
30663  * @class Roo.Resizable
30664  * @extends Roo.util.Observable
30665  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30666  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30667  * 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
30668  * the element will be wrapped for you automatically.</p>
30669  * <p>Here is the list of valid resize handles:</p>
30670  * <pre>
30671 Value   Description
30672 ------  -------------------
30673  'n'     north
30674  's'     south
30675  'e'     east
30676  'w'     west
30677  'nw'    northwest
30678  'sw'    southwest
30679  'se'    southeast
30680  'ne'    northeast
30681  'hd'    horizontal drag
30682  'all'   all
30683 </pre>
30684  * <p>Here's an example showing the creation of a typical Resizable:</p>
30685  * <pre><code>
30686 var resizer = new Roo.Resizable("element-id", {
30687     handles: 'all',
30688     minWidth: 200,
30689     minHeight: 100,
30690     maxWidth: 500,
30691     maxHeight: 400,
30692     pinned: true
30693 });
30694 resizer.on("resize", myHandler);
30695 </code></pre>
30696  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30697  * resizer.east.setDisplayed(false);</p>
30698  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30699  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30700  * resize operation's new size (defaults to [0, 0])
30701  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30702  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30703  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30704  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30705  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30706  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30707  * @cfg {Number} width The width of the element in pixels (defaults to null)
30708  * @cfg {Number} height The height of the element in pixels (defaults to null)
30709  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30710  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30711  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30712  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30713  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30714  * in favor of the handles config option (defaults to false)
30715  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30716  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30717  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30718  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30719  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30720  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30721  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30722  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30723  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30724  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30725  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30726  * @constructor
30727  * Create a new resizable component
30728  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30729  * @param {Object} config configuration options
30730   */
30731 Roo.Resizable = function(el, config)
30732 {
30733     this.el = Roo.get(el);
30734
30735     if(config && config.wrap){
30736         config.resizeChild = this.el;
30737         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30738         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30739         this.el.setStyle("overflow", "hidden");
30740         this.el.setPositioning(config.resizeChild.getPositioning());
30741         config.resizeChild.clearPositioning();
30742         if(!config.width || !config.height){
30743             var csize = config.resizeChild.getSize();
30744             this.el.setSize(csize.width, csize.height);
30745         }
30746         if(config.pinned && !config.adjustments){
30747             config.adjustments = "auto";
30748         }
30749     }
30750
30751     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30752     this.proxy.unselectable();
30753     this.proxy.enableDisplayMode('block');
30754
30755     Roo.apply(this, config);
30756
30757     if(this.pinned){
30758         this.disableTrackOver = true;
30759         this.el.addClass("x-resizable-pinned");
30760     }
30761     // if the element isn't positioned, make it relative
30762     var position = this.el.getStyle("position");
30763     if(position != "absolute" && position != "fixed"){
30764         this.el.setStyle("position", "relative");
30765     }
30766     if(!this.handles){ // no handles passed, must be legacy style
30767         this.handles = 's,e,se';
30768         if(this.multiDirectional){
30769             this.handles += ',n,w';
30770         }
30771     }
30772     if(this.handles == "all"){
30773         this.handles = "n s e w ne nw se sw";
30774     }
30775     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30776     var ps = Roo.Resizable.positions;
30777     for(var i = 0, len = hs.length; i < len; i++){
30778         if(hs[i] && ps[hs[i]]){
30779             var pos = ps[hs[i]];
30780             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30781         }
30782     }
30783     // legacy
30784     this.corner = this.southeast;
30785     
30786     // updateBox = the box can move..
30787     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30788         this.updateBox = true;
30789     }
30790
30791     this.activeHandle = null;
30792
30793     if(this.resizeChild){
30794         if(typeof this.resizeChild == "boolean"){
30795             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30796         }else{
30797             this.resizeChild = Roo.get(this.resizeChild, true);
30798         }
30799     }
30800     
30801     if(this.adjustments == "auto"){
30802         var rc = this.resizeChild;
30803         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30804         if(rc && (hw || hn)){
30805             rc.position("relative");
30806             rc.setLeft(hw ? hw.el.getWidth() : 0);
30807             rc.setTop(hn ? hn.el.getHeight() : 0);
30808         }
30809         this.adjustments = [
30810             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30811             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30812         ];
30813     }
30814
30815     if(this.draggable){
30816         this.dd = this.dynamic ?
30817             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30818         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30819     }
30820
30821     // public events
30822     this.addEvents({
30823         /**
30824          * @event beforeresize
30825          * Fired before resize is allowed. Set enabled to false to cancel resize.
30826          * @param {Roo.Resizable} this
30827          * @param {Roo.EventObject} e The mousedown event
30828          */
30829         "beforeresize" : true,
30830         /**
30831          * @event resizing
30832          * Fired a resizing.
30833          * @param {Roo.Resizable} this
30834          * @param {Number} x The new x position
30835          * @param {Number} y The new y position
30836          * @param {Number} w The new w width
30837          * @param {Number} h The new h hight
30838          * @param {Roo.EventObject} e The mouseup event
30839          */
30840         "resizing" : true,
30841         /**
30842          * @event resize
30843          * Fired after a resize.
30844          * @param {Roo.Resizable} this
30845          * @param {Number} width The new width
30846          * @param {Number} height The new height
30847          * @param {Roo.EventObject} e The mouseup event
30848          */
30849         "resize" : true
30850     });
30851
30852     if(this.width !== null && this.height !== null){
30853         this.resizeTo(this.width, this.height);
30854     }else{
30855         this.updateChildSize();
30856     }
30857     if(Roo.isIE){
30858         this.el.dom.style.zoom = 1;
30859     }
30860     Roo.Resizable.superclass.constructor.call(this);
30861 };
30862
30863 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30864         resizeChild : false,
30865         adjustments : [0, 0],
30866         minWidth : 5,
30867         minHeight : 5,
30868         maxWidth : 10000,
30869         maxHeight : 10000,
30870         enabled : true,
30871         animate : false,
30872         duration : .35,
30873         dynamic : false,
30874         handles : false,
30875         multiDirectional : false,
30876         disableTrackOver : false,
30877         easing : 'easeOutStrong',
30878         widthIncrement : 0,
30879         heightIncrement : 0,
30880         pinned : false,
30881         width : null,
30882         height : null,
30883         preserveRatio : false,
30884         transparent: false,
30885         minX: 0,
30886         minY: 0,
30887         draggable: false,
30888
30889         /**
30890          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30891          */
30892         constrainTo: undefined,
30893         /**
30894          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30895          */
30896         resizeRegion: undefined,
30897
30898
30899     /**
30900      * Perform a manual resize
30901      * @param {Number} width
30902      * @param {Number} height
30903      */
30904     resizeTo : function(width, height){
30905         this.el.setSize(width, height);
30906         this.updateChildSize();
30907         this.fireEvent("resize", this, width, height, null);
30908     },
30909
30910     // private
30911     startSizing : function(e, handle){
30912         this.fireEvent("beforeresize", this, e);
30913         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30914
30915             if(!this.overlay){
30916                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30917                 this.overlay.unselectable();
30918                 this.overlay.enableDisplayMode("block");
30919                 this.overlay.on("mousemove", this.onMouseMove, this);
30920                 this.overlay.on("mouseup", this.onMouseUp, this);
30921             }
30922             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30923
30924             this.resizing = true;
30925             this.startBox = this.el.getBox();
30926             this.startPoint = e.getXY();
30927             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30928                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30929
30930             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30931             this.overlay.show();
30932
30933             if(this.constrainTo) {
30934                 var ct = Roo.get(this.constrainTo);
30935                 this.resizeRegion = ct.getRegion().adjust(
30936                     ct.getFrameWidth('t'),
30937                     ct.getFrameWidth('l'),
30938                     -ct.getFrameWidth('b'),
30939                     -ct.getFrameWidth('r')
30940                 );
30941             }
30942
30943             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30944             this.proxy.show();
30945             this.proxy.setBox(this.startBox);
30946             if(!this.dynamic){
30947                 this.proxy.setStyle('visibility', 'visible');
30948             }
30949         }
30950     },
30951
30952     // private
30953     onMouseDown : function(handle, e){
30954         if(this.enabled){
30955             e.stopEvent();
30956             this.activeHandle = handle;
30957             this.startSizing(e, handle);
30958         }
30959     },
30960
30961     // private
30962     onMouseUp : function(e){
30963         var size = this.resizeElement();
30964         this.resizing = false;
30965         this.handleOut();
30966         this.overlay.hide();
30967         this.proxy.hide();
30968         this.fireEvent("resize", this, size.width, size.height, e);
30969     },
30970
30971     // private
30972     updateChildSize : function(){
30973         
30974         if(this.resizeChild){
30975             var el = this.el;
30976             var child = this.resizeChild;
30977             var adj = this.adjustments;
30978             if(el.dom.offsetWidth){
30979                 var b = el.getSize(true);
30980                 child.setSize(b.width+adj[0], b.height+adj[1]);
30981             }
30982             // Second call here for IE
30983             // The first call enables instant resizing and
30984             // the second call corrects scroll bars if they
30985             // exist
30986             if(Roo.isIE){
30987                 setTimeout(function(){
30988                     if(el.dom.offsetWidth){
30989                         var b = el.getSize(true);
30990                         child.setSize(b.width+adj[0], b.height+adj[1]);
30991                     }
30992                 }, 10);
30993             }
30994         }
30995     },
30996
30997     // private
30998     snap : function(value, inc, min){
30999         if(!inc || !value) {
31000             return value;
31001         }
31002         var newValue = value;
31003         var m = value % inc;
31004         if(m > 0){
31005             if(m > (inc/2)){
31006                 newValue = value + (inc-m);
31007             }else{
31008                 newValue = value - m;
31009             }
31010         }
31011         return Math.max(min, newValue);
31012     },
31013
31014     // private
31015     resizeElement : function(){
31016         var box = this.proxy.getBox();
31017         if(this.updateBox){
31018             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31019         }else{
31020             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31021         }
31022         this.updateChildSize();
31023         if(!this.dynamic){
31024             this.proxy.hide();
31025         }
31026         return box;
31027     },
31028
31029     // private
31030     constrain : function(v, diff, m, mx){
31031         if(v - diff < m){
31032             diff = v - m;
31033         }else if(v - diff > mx){
31034             diff = mx - v;
31035         }
31036         return diff;
31037     },
31038
31039     // private
31040     onMouseMove : function(e){
31041         
31042         if(this.enabled){
31043             try{// try catch so if something goes wrong the user doesn't get hung
31044
31045             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31046                 return;
31047             }
31048
31049             //var curXY = this.startPoint;
31050             var curSize = this.curSize || this.startBox;
31051             var x = this.startBox.x, y = this.startBox.y;
31052             var ox = x, oy = y;
31053             var w = curSize.width, h = curSize.height;
31054             var ow = w, oh = h;
31055             var mw = this.minWidth, mh = this.minHeight;
31056             var mxw = this.maxWidth, mxh = this.maxHeight;
31057             var wi = this.widthIncrement;
31058             var hi = this.heightIncrement;
31059
31060             var eventXY = e.getXY();
31061             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31062             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31063
31064             var pos = this.activeHandle.position;
31065
31066             switch(pos){
31067                 case "east":
31068                     w += diffX;
31069                     w = Math.min(Math.max(mw, w), mxw);
31070                     break;
31071              
31072                 case "south":
31073                     h += diffY;
31074                     h = Math.min(Math.max(mh, h), mxh);
31075                     break;
31076                 case "southeast":
31077                     w += diffX;
31078                     h += diffY;
31079                     w = Math.min(Math.max(mw, w), mxw);
31080                     h = Math.min(Math.max(mh, h), mxh);
31081                     break;
31082                 case "north":
31083                     diffY = this.constrain(h, diffY, mh, mxh);
31084                     y += diffY;
31085                     h -= diffY;
31086                     break;
31087                 case "hdrag":
31088                     
31089                     if (wi) {
31090                         var adiffX = Math.abs(diffX);
31091                         var sub = (adiffX % wi); // how much 
31092                         if (sub > (wi/2)) { // far enough to snap
31093                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31094                         } else {
31095                             // remove difference.. 
31096                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31097                         }
31098                     }
31099                     x += diffX;
31100                     x = Math.max(this.minX, x);
31101                     break;
31102                 case "west":
31103                     diffX = this.constrain(w, diffX, mw, mxw);
31104                     x += diffX;
31105                     w -= diffX;
31106                     break;
31107                 case "northeast":
31108                     w += diffX;
31109                     w = Math.min(Math.max(mw, w), mxw);
31110                     diffY = this.constrain(h, diffY, mh, mxh);
31111                     y += diffY;
31112                     h -= diffY;
31113                     break;
31114                 case "northwest":
31115                     diffX = this.constrain(w, diffX, mw, mxw);
31116                     diffY = this.constrain(h, diffY, mh, mxh);
31117                     y += diffY;
31118                     h -= diffY;
31119                     x += diffX;
31120                     w -= diffX;
31121                     break;
31122                case "southwest":
31123                     diffX = this.constrain(w, diffX, mw, mxw);
31124                     h += diffY;
31125                     h = Math.min(Math.max(mh, h), mxh);
31126                     x += diffX;
31127                     w -= diffX;
31128                     break;
31129             }
31130
31131             var sw = this.snap(w, wi, mw);
31132             var sh = this.snap(h, hi, mh);
31133             if(sw != w || sh != h){
31134                 switch(pos){
31135                     case "northeast":
31136                         y -= sh - h;
31137                     break;
31138                     case "north":
31139                         y -= sh - h;
31140                         break;
31141                     case "southwest":
31142                         x -= sw - w;
31143                     break;
31144                     case "west":
31145                         x -= sw - w;
31146                         break;
31147                     case "northwest":
31148                         x -= sw - w;
31149                         y -= sh - h;
31150                     break;
31151                 }
31152                 w = sw;
31153                 h = sh;
31154             }
31155
31156             if(this.preserveRatio){
31157                 switch(pos){
31158                     case "southeast":
31159                     case "east":
31160                         h = oh * (w/ow);
31161                         h = Math.min(Math.max(mh, h), mxh);
31162                         w = ow * (h/oh);
31163                        break;
31164                     case "south":
31165                         w = ow * (h/oh);
31166                         w = Math.min(Math.max(mw, w), mxw);
31167                         h = oh * (w/ow);
31168                         break;
31169                     case "northeast":
31170                         w = ow * (h/oh);
31171                         w = Math.min(Math.max(mw, w), mxw);
31172                         h = oh * (w/ow);
31173                     break;
31174                     case "north":
31175                         var tw = w;
31176                         w = ow * (h/oh);
31177                         w = Math.min(Math.max(mw, w), mxw);
31178                         h = oh * (w/ow);
31179                         x += (tw - w) / 2;
31180                         break;
31181                     case "southwest":
31182                         h = oh * (w/ow);
31183                         h = Math.min(Math.max(mh, h), mxh);
31184                         var tw = w;
31185                         w = ow * (h/oh);
31186                         x += tw - w;
31187                         break;
31188                     case "west":
31189                         var th = h;
31190                         h = oh * (w/ow);
31191                         h = Math.min(Math.max(mh, h), mxh);
31192                         y += (th - h) / 2;
31193                         var tw = w;
31194                         w = ow * (h/oh);
31195                         x += tw - w;
31196                        break;
31197                     case "northwest":
31198                         var tw = w;
31199                         var th = h;
31200                         h = oh * (w/ow);
31201                         h = Math.min(Math.max(mh, h), mxh);
31202                         w = ow * (h/oh);
31203                         y += th - h;
31204                         x += tw - w;
31205                        break;
31206
31207                 }
31208             }
31209             if (pos == 'hdrag') {
31210                 w = ow;
31211             }
31212             this.proxy.setBounds(x, y, w, h);
31213             if(this.dynamic){
31214                 this.resizeElement();
31215             }
31216             }catch(e){}
31217         }
31218         this.fireEvent("resizing", this, x, y, w, h, e);
31219     },
31220
31221     // private
31222     handleOver : function(){
31223         if(this.enabled){
31224             this.el.addClass("x-resizable-over");
31225         }
31226     },
31227
31228     // private
31229     handleOut : function(){
31230         if(!this.resizing){
31231             this.el.removeClass("x-resizable-over");
31232         }
31233     },
31234
31235     /**
31236      * Returns the element this component is bound to.
31237      * @return {Roo.Element}
31238      */
31239     getEl : function(){
31240         return this.el;
31241     },
31242
31243     /**
31244      * Returns the resizeChild element (or null).
31245      * @return {Roo.Element}
31246      */
31247     getResizeChild : function(){
31248         return this.resizeChild;
31249     },
31250     groupHandler : function()
31251     {
31252         
31253     },
31254     /**
31255      * Destroys this resizable. If the element was wrapped and
31256      * removeEl is not true then the element remains.
31257      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31258      */
31259     destroy : function(removeEl){
31260         this.proxy.remove();
31261         if(this.overlay){
31262             this.overlay.removeAllListeners();
31263             this.overlay.remove();
31264         }
31265         var ps = Roo.Resizable.positions;
31266         for(var k in ps){
31267             if(typeof ps[k] != "function" && this[ps[k]]){
31268                 var h = this[ps[k]];
31269                 h.el.removeAllListeners();
31270                 h.el.remove();
31271             }
31272         }
31273         if(removeEl){
31274             this.el.update("");
31275             this.el.remove();
31276         }
31277     }
31278 });
31279
31280 // private
31281 // hash to map config positions to true positions
31282 Roo.Resizable.positions = {
31283     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31284     hd: "hdrag"
31285 };
31286
31287 // private
31288 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31289     if(!this.tpl){
31290         // only initialize the template if resizable is used
31291         var tpl = Roo.DomHelper.createTemplate(
31292             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31293         );
31294         tpl.compile();
31295         Roo.Resizable.Handle.prototype.tpl = tpl;
31296     }
31297     this.position = pos;
31298     this.rz = rz;
31299     // show north drag fro topdra
31300     var handlepos = pos == 'hdrag' ? 'north' : pos;
31301     
31302     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31303     if (pos == 'hdrag') {
31304         this.el.setStyle('cursor', 'pointer');
31305     }
31306     this.el.unselectable();
31307     if(transparent){
31308         this.el.setOpacity(0);
31309     }
31310     this.el.on("mousedown", this.onMouseDown, this);
31311     if(!disableTrackOver){
31312         this.el.on("mouseover", this.onMouseOver, this);
31313         this.el.on("mouseout", this.onMouseOut, this);
31314     }
31315 };
31316
31317 // private
31318 Roo.Resizable.Handle.prototype = {
31319     afterResize : function(rz){
31320         Roo.log('after?');
31321         // do nothing
31322     },
31323     // private
31324     onMouseDown : function(e){
31325         this.rz.onMouseDown(this, e);
31326     },
31327     // private
31328     onMouseOver : function(e){
31329         this.rz.handleOver(this, e);
31330     },
31331     // private
31332     onMouseOut : function(e){
31333         this.rz.handleOut(this, e);
31334     }
31335 };/*
31336  * Based on:
31337  * Ext JS Library 1.1.1
31338  * Copyright(c) 2006-2007, Ext JS, LLC.
31339  *
31340  * Originally Released Under LGPL - original licence link has changed is not relivant.
31341  *
31342  * Fork - LGPL
31343  * <script type="text/javascript">
31344  */
31345
31346 /**
31347  * @class Roo.Editor
31348  * @extends Roo.Component
31349  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31350  * @constructor
31351  * Create a new Editor
31352  * @param {Roo.form.Field} field The Field object (or descendant)
31353  * @param {Object} config The config object
31354  */
31355 Roo.Editor = function(field, config){
31356     Roo.Editor.superclass.constructor.call(this, config);
31357     this.field = field;
31358     this.addEvents({
31359         /**
31360              * @event beforestartedit
31361              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31362              * false from the handler of this event.
31363              * @param {Editor} this
31364              * @param {Roo.Element} boundEl The underlying element bound to this editor
31365              * @param {Mixed} value The field value being set
31366              */
31367         "beforestartedit" : true,
31368         /**
31369              * @event startedit
31370              * Fires when this editor is displayed
31371              * @param {Roo.Element} boundEl The underlying element bound to this editor
31372              * @param {Mixed} value The starting field value
31373              */
31374         "startedit" : true,
31375         /**
31376              * @event beforecomplete
31377              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31378              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31379              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31380              * event will not fire since no edit actually occurred.
31381              * @param {Editor} this
31382              * @param {Mixed} value The current field value
31383              * @param {Mixed} startValue The original field value
31384              */
31385         "beforecomplete" : true,
31386         /**
31387              * @event complete
31388              * Fires after editing is complete and any changed value has been written to the underlying field.
31389              * @param {Editor} this
31390              * @param {Mixed} value The current field value
31391              * @param {Mixed} startValue The original field value
31392              */
31393         "complete" : true,
31394         /**
31395          * @event specialkey
31396          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31397          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31398          * @param {Roo.form.Field} this
31399          * @param {Roo.EventObject} e The event object
31400          */
31401         "specialkey" : true
31402     });
31403 };
31404
31405 Roo.extend(Roo.Editor, Roo.Component, {
31406     /**
31407      * @cfg {Boolean/String} autosize
31408      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31409      * or "height" to adopt the height only (defaults to false)
31410      */
31411     /**
31412      * @cfg {Boolean} revertInvalid
31413      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31414      * validation fails (defaults to true)
31415      */
31416     /**
31417      * @cfg {Boolean} ignoreNoChange
31418      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31419      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31420      * will never be ignored.
31421      */
31422     /**
31423      * @cfg {Boolean} hideEl
31424      * False to keep the bound element visible while the editor is displayed (defaults to true)
31425      */
31426     /**
31427      * @cfg {Mixed} value
31428      * The data value of the underlying field (defaults to "")
31429      */
31430     value : "",
31431     /**
31432      * @cfg {String} alignment
31433      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31434      */
31435     alignment: "c-c?",
31436     /**
31437      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31438      * for bottom-right shadow (defaults to "frame")
31439      */
31440     shadow : "frame",
31441     /**
31442      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31443      */
31444     constrain : false,
31445     /**
31446      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31447      */
31448     completeOnEnter : false,
31449     /**
31450      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31451      */
31452     cancelOnEsc : false,
31453     /**
31454      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31455      */
31456     updateEl : false,
31457
31458     // private
31459     onRender : function(ct, position){
31460         this.el = new Roo.Layer({
31461             shadow: this.shadow,
31462             cls: "x-editor",
31463             parentEl : ct,
31464             shim : this.shim,
31465             shadowOffset:4,
31466             id: this.id,
31467             constrain: this.constrain
31468         });
31469         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31470         if(this.field.msgTarget != 'title'){
31471             this.field.msgTarget = 'qtip';
31472         }
31473         this.field.render(this.el);
31474         if(Roo.isGecko){
31475             this.field.el.dom.setAttribute('autocomplete', 'off');
31476         }
31477         this.field.on("specialkey", this.onSpecialKey, this);
31478         if(this.swallowKeys){
31479             this.field.el.swallowEvent(['keydown','keypress']);
31480         }
31481         this.field.show();
31482         this.field.on("blur", this.onBlur, this);
31483         if(this.field.grow){
31484             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31485         }
31486     },
31487
31488     onSpecialKey : function(field, e)
31489     {
31490         //Roo.log('editor onSpecialKey');
31491         if(this.completeOnEnter && e.getKey() == e.ENTER){
31492             e.stopEvent();
31493             this.completeEdit();
31494             return;
31495         }
31496         // do not fire special key otherwise it might hide close the editor...
31497         if(e.getKey() == e.ENTER){    
31498             return;
31499         }
31500         if(this.cancelOnEsc && e.getKey() == e.ESC){
31501             this.cancelEdit();
31502             return;
31503         } 
31504         this.fireEvent('specialkey', field, e);
31505     
31506     },
31507
31508     /**
31509      * Starts the editing process and shows the editor.
31510      * @param {String/HTMLElement/Element} el The element to edit
31511      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31512       * to the innerHTML of el.
31513      */
31514     startEdit : function(el, value){
31515         if(this.editing){
31516             this.completeEdit();
31517         }
31518         this.boundEl = Roo.get(el);
31519         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31520         if(!this.rendered){
31521             this.render(this.parentEl || document.body);
31522         }
31523         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31524             return;
31525         }
31526         this.startValue = v;
31527         this.field.setValue(v);
31528         if(this.autoSize){
31529             var sz = this.boundEl.getSize();
31530             switch(this.autoSize){
31531                 case "width":
31532                 this.setSize(sz.width,  "");
31533                 break;
31534                 case "height":
31535                 this.setSize("",  sz.height);
31536                 break;
31537                 default:
31538                 this.setSize(sz.width,  sz.height);
31539             }
31540         }
31541         this.el.alignTo(this.boundEl, this.alignment);
31542         this.editing = true;
31543         if(Roo.QuickTips){
31544             Roo.QuickTips.disable();
31545         }
31546         this.show();
31547     },
31548
31549     /**
31550      * Sets the height and width of this editor.
31551      * @param {Number} width The new width
31552      * @param {Number} height The new height
31553      */
31554     setSize : function(w, h){
31555         this.field.setSize(w, h);
31556         if(this.el){
31557             this.el.sync();
31558         }
31559     },
31560
31561     /**
31562      * Realigns the editor to the bound field based on the current alignment config value.
31563      */
31564     realign : function(){
31565         this.el.alignTo(this.boundEl, this.alignment);
31566     },
31567
31568     /**
31569      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31570      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31571      */
31572     completeEdit : function(remainVisible){
31573         if(!this.editing){
31574             return;
31575         }
31576         var v = this.getValue();
31577         if(this.revertInvalid !== false && !this.field.isValid()){
31578             v = this.startValue;
31579             this.cancelEdit(true);
31580         }
31581         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31582             this.editing = false;
31583             this.hide();
31584             return;
31585         }
31586         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31587             this.editing = false;
31588             if(this.updateEl && this.boundEl){
31589                 this.boundEl.update(v);
31590             }
31591             if(remainVisible !== true){
31592                 this.hide();
31593             }
31594             this.fireEvent("complete", this, v, this.startValue);
31595         }
31596     },
31597
31598     // private
31599     onShow : function(){
31600         this.el.show();
31601         if(this.hideEl !== false){
31602             this.boundEl.hide();
31603         }
31604         this.field.show();
31605         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31606             this.fixIEFocus = true;
31607             this.deferredFocus.defer(50, this);
31608         }else{
31609             this.field.focus();
31610         }
31611         this.fireEvent("startedit", this.boundEl, this.startValue);
31612     },
31613
31614     deferredFocus : function(){
31615         if(this.editing){
31616             this.field.focus();
31617         }
31618     },
31619
31620     /**
31621      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31622      * reverted to the original starting value.
31623      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31624      * cancel (defaults to false)
31625      */
31626     cancelEdit : function(remainVisible){
31627         if(this.editing){
31628             this.setValue(this.startValue);
31629             if(remainVisible !== true){
31630                 this.hide();
31631             }
31632         }
31633     },
31634
31635     // private
31636     onBlur : function(){
31637         if(this.allowBlur !== true && this.editing){
31638             this.completeEdit();
31639         }
31640     },
31641
31642     // private
31643     onHide : function(){
31644         if(this.editing){
31645             this.completeEdit();
31646             return;
31647         }
31648         this.field.blur();
31649         if(this.field.collapse){
31650             this.field.collapse();
31651         }
31652         this.el.hide();
31653         if(this.hideEl !== false){
31654             this.boundEl.show();
31655         }
31656         if(Roo.QuickTips){
31657             Roo.QuickTips.enable();
31658         }
31659     },
31660
31661     /**
31662      * Sets the data value of the editor
31663      * @param {Mixed} value Any valid value supported by the underlying field
31664      */
31665     setValue : function(v){
31666         this.field.setValue(v);
31667     },
31668
31669     /**
31670      * Gets the data value of the editor
31671      * @return {Mixed} The data value
31672      */
31673     getValue : function(){
31674         return this.field.getValue();
31675     }
31676 });/*
31677  * Based on:
31678  * Ext JS Library 1.1.1
31679  * Copyright(c) 2006-2007, Ext JS, LLC.
31680  *
31681  * Originally Released Under LGPL - original licence link has changed is not relivant.
31682  *
31683  * Fork - LGPL
31684  * <script type="text/javascript">
31685  */
31686  
31687 /**
31688  * @class Roo.BasicDialog
31689  * @extends Roo.util.Observable
31690  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31691  * <pre><code>
31692 var dlg = new Roo.BasicDialog("my-dlg", {
31693     height: 200,
31694     width: 300,
31695     minHeight: 100,
31696     minWidth: 150,
31697     modal: true,
31698     proxyDrag: true,
31699     shadow: true
31700 });
31701 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31702 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31703 dlg.addButton('Cancel', dlg.hide, dlg);
31704 dlg.show();
31705 </code></pre>
31706   <b>A Dialog should always be a direct child of the body element.</b>
31707  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31708  * @cfg {String} title Default text to display in the title bar (defaults to null)
31709  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31710  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31711  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31712  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31713  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31714  * (defaults to null with no animation)
31715  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31716  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31717  * property for valid values (defaults to 'all')
31718  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31719  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31720  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31721  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31722  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31723  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31724  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31725  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31726  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31727  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31728  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31729  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31730  * draggable = true (defaults to false)
31731  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31732  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31733  * shadow (defaults to false)
31734  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31735  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31736  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31737  * @cfg {Array} buttons Array of buttons
31738  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31739  * @constructor
31740  * Create a new BasicDialog.
31741  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31742  * @param {Object} config Configuration options
31743  */
31744 Roo.BasicDialog = function(el, config){
31745     this.el = Roo.get(el);
31746     var dh = Roo.DomHelper;
31747     if(!this.el && config && config.autoCreate){
31748         if(typeof config.autoCreate == "object"){
31749             if(!config.autoCreate.id){
31750                 config.autoCreate.id = el;
31751             }
31752             this.el = dh.append(document.body,
31753                         config.autoCreate, true);
31754         }else{
31755             this.el = dh.append(document.body,
31756                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31757         }
31758     }
31759     el = this.el;
31760     el.setDisplayed(true);
31761     el.hide = this.hideAction;
31762     this.id = el.id;
31763     el.addClass("x-dlg");
31764
31765     Roo.apply(this, config);
31766
31767     this.proxy = el.createProxy("x-dlg-proxy");
31768     this.proxy.hide = this.hideAction;
31769     this.proxy.setOpacity(.5);
31770     this.proxy.hide();
31771
31772     if(config.width){
31773         el.setWidth(config.width);
31774     }
31775     if(config.height){
31776         el.setHeight(config.height);
31777     }
31778     this.size = el.getSize();
31779     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31780         this.xy = [config.x,config.y];
31781     }else{
31782         this.xy = el.getCenterXY(true);
31783     }
31784     /** The header element @type Roo.Element */
31785     this.header = el.child("> .x-dlg-hd");
31786     /** The body element @type Roo.Element */
31787     this.body = el.child("> .x-dlg-bd");
31788     /** The footer element @type Roo.Element */
31789     this.footer = el.child("> .x-dlg-ft");
31790
31791     if(!this.header){
31792         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31793     }
31794     if(!this.body){
31795         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31796     }
31797
31798     this.header.unselectable();
31799     if(this.title){
31800         this.header.update(this.title);
31801     }
31802     // this element allows the dialog to be focused for keyboard event
31803     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31804     this.focusEl.swallowEvent("click", true);
31805
31806     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31807
31808     // wrap the body and footer for special rendering
31809     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31810     if(this.footer){
31811         this.bwrap.dom.appendChild(this.footer.dom);
31812     }
31813
31814     this.bg = this.el.createChild({
31815         tag: "div", cls:"x-dlg-bg",
31816         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31817     });
31818     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31819
31820
31821     if(this.autoScroll !== false && !this.autoTabs){
31822         this.body.setStyle("overflow", "auto");
31823     }
31824
31825     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31826
31827     if(this.closable !== false){
31828         this.el.addClass("x-dlg-closable");
31829         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31830         this.close.on("click", this.closeClick, this);
31831         this.close.addClassOnOver("x-dlg-close-over");
31832     }
31833     if(this.collapsible !== false){
31834         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31835         this.collapseBtn.on("click", this.collapseClick, this);
31836         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31837         this.header.on("dblclick", this.collapseClick, this);
31838     }
31839     if(this.resizable !== false){
31840         this.el.addClass("x-dlg-resizable");
31841         this.resizer = new Roo.Resizable(el, {
31842             minWidth: this.minWidth || 80,
31843             minHeight:this.minHeight || 80,
31844             handles: this.resizeHandles || "all",
31845             pinned: true
31846         });
31847         this.resizer.on("beforeresize", this.beforeResize, this);
31848         this.resizer.on("resize", this.onResize, this);
31849     }
31850     if(this.draggable !== false){
31851         el.addClass("x-dlg-draggable");
31852         if (!this.proxyDrag) {
31853             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31854         }
31855         else {
31856             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31857         }
31858         dd.setHandleElId(this.header.id);
31859         dd.endDrag = this.endMove.createDelegate(this);
31860         dd.startDrag = this.startMove.createDelegate(this);
31861         dd.onDrag = this.onDrag.createDelegate(this);
31862         dd.scroll = false;
31863         this.dd = dd;
31864     }
31865     if(this.modal){
31866         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31867         this.mask.enableDisplayMode("block");
31868         this.mask.hide();
31869         this.el.addClass("x-dlg-modal");
31870     }
31871     if(this.shadow){
31872         this.shadow = new Roo.Shadow({
31873             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31874             offset : this.shadowOffset
31875         });
31876     }else{
31877         this.shadowOffset = 0;
31878     }
31879     if(Roo.useShims && this.shim !== false){
31880         this.shim = this.el.createShim();
31881         this.shim.hide = this.hideAction;
31882         this.shim.hide();
31883     }else{
31884         this.shim = false;
31885     }
31886     if(this.autoTabs){
31887         this.initTabs();
31888     }
31889     if (this.buttons) { 
31890         var bts= this.buttons;
31891         this.buttons = [];
31892         Roo.each(bts, function(b) {
31893             this.addButton(b);
31894         }, this);
31895     }
31896     
31897     
31898     this.addEvents({
31899         /**
31900          * @event keydown
31901          * Fires when a key is pressed
31902          * @param {Roo.BasicDialog} this
31903          * @param {Roo.EventObject} e
31904          */
31905         "keydown" : true,
31906         /**
31907          * @event move
31908          * Fires when this dialog is moved by the user.
31909          * @param {Roo.BasicDialog} this
31910          * @param {Number} x The new page X
31911          * @param {Number} y The new page Y
31912          */
31913         "move" : true,
31914         /**
31915          * @event resize
31916          * Fires when this dialog is resized by the user.
31917          * @param {Roo.BasicDialog} this
31918          * @param {Number} width The new width
31919          * @param {Number} height The new height
31920          */
31921         "resize" : true,
31922         /**
31923          * @event beforehide
31924          * Fires before this dialog is hidden.
31925          * @param {Roo.BasicDialog} this
31926          */
31927         "beforehide" : true,
31928         /**
31929          * @event hide
31930          * Fires when this dialog is hidden.
31931          * @param {Roo.BasicDialog} this
31932          */
31933         "hide" : true,
31934         /**
31935          * @event beforeshow
31936          * Fires before this dialog is shown.
31937          * @param {Roo.BasicDialog} this
31938          */
31939         "beforeshow" : true,
31940         /**
31941          * @event show
31942          * Fires when this dialog is shown.
31943          * @param {Roo.BasicDialog} this
31944          */
31945         "show" : true
31946     });
31947     el.on("keydown", this.onKeyDown, this);
31948     el.on("mousedown", this.toFront, this);
31949     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31950     this.el.hide();
31951     Roo.DialogManager.register(this);
31952     Roo.BasicDialog.superclass.constructor.call(this);
31953 };
31954
31955 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31956     shadowOffset: Roo.isIE ? 6 : 5,
31957     minHeight: 80,
31958     minWidth: 200,
31959     minButtonWidth: 75,
31960     defaultButton: null,
31961     buttonAlign: "right",
31962     tabTag: 'div',
31963     firstShow: true,
31964
31965     /**
31966      * Sets the dialog title text
31967      * @param {String} text The title text to display
31968      * @return {Roo.BasicDialog} this
31969      */
31970     setTitle : function(text){
31971         this.header.update(text);
31972         return this;
31973     },
31974
31975     // private
31976     closeClick : function(){
31977         this.hide();
31978     },
31979
31980     // private
31981     collapseClick : function(){
31982         this[this.collapsed ? "expand" : "collapse"]();
31983     },
31984
31985     /**
31986      * Collapses the dialog to its minimized state (only the title bar is visible).
31987      * Equivalent to the user clicking the collapse dialog button.
31988      */
31989     collapse : function(){
31990         if(!this.collapsed){
31991             this.collapsed = true;
31992             this.el.addClass("x-dlg-collapsed");
31993             this.restoreHeight = this.el.getHeight();
31994             this.resizeTo(this.el.getWidth(), this.header.getHeight());
31995         }
31996     },
31997
31998     /**
31999      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32000      * clicking the expand dialog button.
32001      */
32002     expand : function(){
32003         if(this.collapsed){
32004             this.collapsed = false;
32005             this.el.removeClass("x-dlg-collapsed");
32006             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32007         }
32008     },
32009
32010     /**
32011      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32012      * @return {Roo.TabPanel} The tabs component
32013      */
32014     initTabs : function(){
32015         var tabs = this.getTabs();
32016         while(tabs.getTab(0)){
32017             tabs.removeTab(0);
32018         }
32019         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32020             var dom = el.dom;
32021             tabs.addTab(Roo.id(dom), dom.title);
32022             dom.title = "";
32023         });
32024         tabs.activate(0);
32025         return tabs;
32026     },
32027
32028     // private
32029     beforeResize : function(){
32030         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32031     },
32032
32033     // private
32034     onResize : function(){
32035         this.refreshSize();
32036         this.syncBodyHeight();
32037         this.adjustAssets();
32038         this.focus();
32039         this.fireEvent("resize", this, this.size.width, this.size.height);
32040     },
32041
32042     // private
32043     onKeyDown : function(e){
32044         if(this.isVisible()){
32045             this.fireEvent("keydown", this, e);
32046         }
32047     },
32048
32049     /**
32050      * Resizes the dialog.
32051      * @param {Number} width
32052      * @param {Number} height
32053      * @return {Roo.BasicDialog} this
32054      */
32055     resizeTo : function(width, height){
32056         this.el.setSize(width, height);
32057         this.size = {width: width, height: height};
32058         this.syncBodyHeight();
32059         if(this.fixedcenter){
32060             this.center();
32061         }
32062         if(this.isVisible()){
32063             this.constrainXY();
32064             this.adjustAssets();
32065         }
32066         this.fireEvent("resize", this, width, height);
32067         return this;
32068     },
32069
32070
32071     /**
32072      * Resizes the dialog to fit the specified content size.
32073      * @param {Number} width
32074      * @param {Number} height
32075      * @return {Roo.BasicDialog} this
32076      */
32077     setContentSize : function(w, h){
32078         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32079         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32080         //if(!this.el.isBorderBox()){
32081             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32082             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32083         //}
32084         if(this.tabs){
32085             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32086             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32087         }
32088         this.resizeTo(w, h);
32089         return this;
32090     },
32091
32092     /**
32093      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32094      * executed in response to a particular key being pressed while the dialog is active.
32095      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32096      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32097      * @param {Function} fn The function to call
32098      * @param {Object} scope (optional) The scope of the function
32099      * @return {Roo.BasicDialog} this
32100      */
32101     addKeyListener : function(key, fn, scope){
32102         var keyCode, shift, ctrl, alt;
32103         if(typeof key == "object" && !(key instanceof Array)){
32104             keyCode = key["key"];
32105             shift = key["shift"];
32106             ctrl = key["ctrl"];
32107             alt = key["alt"];
32108         }else{
32109             keyCode = key;
32110         }
32111         var handler = function(dlg, e){
32112             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32113                 var k = e.getKey();
32114                 if(keyCode instanceof Array){
32115                     for(var i = 0, len = keyCode.length; i < len; i++){
32116                         if(keyCode[i] == k){
32117                           fn.call(scope || window, dlg, k, e);
32118                           return;
32119                         }
32120                     }
32121                 }else{
32122                     if(k == keyCode){
32123                         fn.call(scope || window, dlg, k, e);
32124                     }
32125                 }
32126             }
32127         };
32128         this.on("keydown", handler);
32129         return this;
32130     },
32131
32132     /**
32133      * Returns the TabPanel component (creates it if it doesn't exist).
32134      * Note: If you wish to simply check for the existence of tabs without creating them,
32135      * check for a null 'tabs' property.
32136      * @return {Roo.TabPanel} The tabs component
32137      */
32138     getTabs : function(){
32139         if(!this.tabs){
32140             this.el.addClass("x-dlg-auto-tabs");
32141             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32142             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32143         }
32144         return this.tabs;
32145     },
32146
32147     /**
32148      * Adds a button to the footer section of the dialog.
32149      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32150      * object or a valid Roo.DomHelper element config
32151      * @param {Function} handler The function called when the button is clicked
32152      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32153      * @return {Roo.Button} The new button
32154      */
32155     addButton : function(config, handler, scope){
32156         var dh = Roo.DomHelper;
32157         if(!this.footer){
32158             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32159         }
32160         if(!this.btnContainer){
32161             var tb = this.footer.createChild({
32162
32163                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32164                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32165             }, null, true);
32166             this.btnContainer = tb.firstChild.firstChild.firstChild;
32167         }
32168         var bconfig = {
32169             handler: handler,
32170             scope: scope,
32171             minWidth: this.minButtonWidth,
32172             hideParent:true
32173         };
32174         if(typeof config == "string"){
32175             bconfig.text = config;
32176         }else{
32177             if(config.tag){
32178                 bconfig.dhconfig = config;
32179             }else{
32180                 Roo.apply(bconfig, config);
32181             }
32182         }
32183         var fc = false;
32184         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32185             bconfig.position = Math.max(0, bconfig.position);
32186             fc = this.btnContainer.childNodes[bconfig.position];
32187         }
32188          
32189         var btn = new Roo.Button(
32190             fc ? 
32191                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32192                 : this.btnContainer.appendChild(document.createElement("td")),
32193             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32194             bconfig
32195         );
32196         this.syncBodyHeight();
32197         if(!this.buttons){
32198             /**
32199              * Array of all the buttons that have been added to this dialog via addButton
32200              * @type Array
32201              */
32202             this.buttons = [];
32203         }
32204         this.buttons.push(btn);
32205         return btn;
32206     },
32207
32208     /**
32209      * Sets the default button to be focused when the dialog is displayed.
32210      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32211      * @return {Roo.BasicDialog} this
32212      */
32213     setDefaultButton : function(btn){
32214         this.defaultButton = btn;
32215         return this;
32216     },
32217
32218     // private
32219     getHeaderFooterHeight : function(safe){
32220         var height = 0;
32221         if(this.header){
32222            height += this.header.getHeight();
32223         }
32224         if(this.footer){
32225            var fm = this.footer.getMargins();
32226             height += (this.footer.getHeight()+fm.top+fm.bottom);
32227         }
32228         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32229         height += this.centerBg.getPadding("tb");
32230         return height;
32231     },
32232
32233     // private
32234     syncBodyHeight : function()
32235     {
32236         var bd = this.body, // the text
32237             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32238             bw = this.bwrap;
32239         var height = this.size.height - this.getHeaderFooterHeight(false);
32240         bd.setHeight(height-bd.getMargins("tb"));
32241         var hh = this.header.getHeight();
32242         var h = this.size.height-hh;
32243         cb.setHeight(h);
32244         
32245         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32246         bw.setHeight(h-cb.getPadding("tb"));
32247         
32248         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32249         bd.setWidth(bw.getWidth(true));
32250         if(this.tabs){
32251             this.tabs.syncHeight();
32252             if(Roo.isIE){
32253                 this.tabs.el.repaint();
32254             }
32255         }
32256     },
32257
32258     /**
32259      * Restores the previous state of the dialog if Roo.state is configured.
32260      * @return {Roo.BasicDialog} this
32261      */
32262     restoreState : function(){
32263         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32264         if(box && box.width){
32265             this.xy = [box.x, box.y];
32266             this.resizeTo(box.width, box.height);
32267         }
32268         return this;
32269     },
32270
32271     // private
32272     beforeShow : function(){
32273         this.expand();
32274         if(this.fixedcenter){
32275             this.xy = this.el.getCenterXY(true);
32276         }
32277         if(this.modal){
32278             Roo.get(document.body).addClass("x-body-masked");
32279             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32280             this.mask.show();
32281         }
32282         this.constrainXY();
32283     },
32284
32285     // private
32286     animShow : function(){
32287         var b = Roo.get(this.animateTarget).getBox();
32288         this.proxy.setSize(b.width, b.height);
32289         this.proxy.setLocation(b.x, b.y);
32290         this.proxy.show();
32291         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32292                     true, .35, this.showEl.createDelegate(this));
32293     },
32294
32295     /**
32296      * Shows the dialog.
32297      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32298      * @return {Roo.BasicDialog} this
32299      */
32300     show : function(animateTarget){
32301         if (this.fireEvent("beforeshow", this) === false){
32302             return;
32303         }
32304         if(this.syncHeightBeforeShow){
32305             this.syncBodyHeight();
32306         }else if(this.firstShow){
32307             this.firstShow = false;
32308             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32309         }
32310         this.animateTarget = animateTarget || this.animateTarget;
32311         if(!this.el.isVisible()){
32312             this.beforeShow();
32313             if(this.animateTarget && Roo.get(this.animateTarget)){
32314                 this.animShow();
32315             }else{
32316                 this.showEl();
32317             }
32318         }
32319         return this;
32320     },
32321
32322     // private
32323     showEl : function(){
32324         this.proxy.hide();
32325         this.el.setXY(this.xy);
32326         this.el.show();
32327         this.adjustAssets(true);
32328         this.toFront();
32329         this.focus();
32330         // IE peekaboo bug - fix found by Dave Fenwick
32331         if(Roo.isIE){
32332             this.el.repaint();
32333         }
32334         this.fireEvent("show", this);
32335     },
32336
32337     /**
32338      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32339      * dialog itself will receive focus.
32340      */
32341     focus : function(){
32342         if(this.defaultButton){
32343             this.defaultButton.focus();
32344         }else{
32345             this.focusEl.focus();
32346         }
32347     },
32348
32349     // private
32350     constrainXY : function(){
32351         if(this.constraintoviewport !== false){
32352             if(!this.viewSize){
32353                 if(this.container){
32354                     var s = this.container.getSize();
32355                     this.viewSize = [s.width, s.height];
32356                 }else{
32357                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32358                 }
32359             }
32360             var s = Roo.get(this.container||document).getScroll();
32361
32362             var x = this.xy[0], y = this.xy[1];
32363             var w = this.size.width, h = this.size.height;
32364             var vw = this.viewSize[0], vh = this.viewSize[1];
32365             // only move it if it needs it
32366             var moved = false;
32367             // first validate right/bottom
32368             if(x + w > vw+s.left){
32369                 x = vw - w;
32370                 moved = true;
32371             }
32372             if(y + h > vh+s.top){
32373                 y = vh - h;
32374                 moved = true;
32375             }
32376             // then make sure top/left isn't negative
32377             if(x < s.left){
32378                 x = s.left;
32379                 moved = true;
32380             }
32381             if(y < s.top){
32382                 y = s.top;
32383                 moved = true;
32384             }
32385             if(moved){
32386                 // cache xy
32387                 this.xy = [x, y];
32388                 if(this.isVisible()){
32389                     this.el.setLocation(x, y);
32390                     this.adjustAssets();
32391                 }
32392             }
32393         }
32394     },
32395
32396     // private
32397     onDrag : function(){
32398         if(!this.proxyDrag){
32399             this.xy = this.el.getXY();
32400             this.adjustAssets();
32401         }
32402     },
32403
32404     // private
32405     adjustAssets : function(doShow){
32406         var x = this.xy[0], y = this.xy[1];
32407         var w = this.size.width, h = this.size.height;
32408         if(doShow === true){
32409             if(this.shadow){
32410                 this.shadow.show(this.el);
32411             }
32412             if(this.shim){
32413                 this.shim.show();
32414             }
32415         }
32416         if(this.shadow && this.shadow.isVisible()){
32417             this.shadow.show(this.el);
32418         }
32419         if(this.shim && this.shim.isVisible()){
32420             this.shim.setBounds(x, y, w, h);
32421         }
32422     },
32423
32424     // private
32425     adjustViewport : function(w, h){
32426         if(!w || !h){
32427             w = Roo.lib.Dom.getViewWidth();
32428             h = Roo.lib.Dom.getViewHeight();
32429         }
32430         // cache the size
32431         this.viewSize = [w, h];
32432         if(this.modal && this.mask.isVisible()){
32433             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32434             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32435         }
32436         if(this.isVisible()){
32437             this.constrainXY();
32438         }
32439     },
32440
32441     /**
32442      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32443      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32444      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32445      */
32446     destroy : function(removeEl){
32447         if(this.isVisible()){
32448             this.animateTarget = null;
32449             this.hide();
32450         }
32451         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32452         if(this.tabs){
32453             this.tabs.destroy(removeEl);
32454         }
32455         Roo.destroy(
32456              this.shim,
32457              this.proxy,
32458              this.resizer,
32459              this.close,
32460              this.mask
32461         );
32462         if(this.dd){
32463             this.dd.unreg();
32464         }
32465         if(this.buttons){
32466            for(var i = 0, len = this.buttons.length; i < len; i++){
32467                this.buttons[i].destroy();
32468            }
32469         }
32470         this.el.removeAllListeners();
32471         if(removeEl === true){
32472             this.el.update("");
32473             this.el.remove();
32474         }
32475         Roo.DialogManager.unregister(this);
32476     },
32477
32478     // private
32479     startMove : function(){
32480         if(this.proxyDrag){
32481             this.proxy.show();
32482         }
32483         if(this.constraintoviewport !== false){
32484             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32485         }
32486     },
32487
32488     // private
32489     endMove : function(){
32490         if(!this.proxyDrag){
32491             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32492         }else{
32493             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32494             this.proxy.hide();
32495         }
32496         this.refreshSize();
32497         this.adjustAssets();
32498         this.focus();
32499         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32500     },
32501
32502     /**
32503      * Brings this dialog to the front of any other visible dialogs
32504      * @return {Roo.BasicDialog} this
32505      */
32506     toFront : function(){
32507         Roo.DialogManager.bringToFront(this);
32508         return this;
32509     },
32510
32511     /**
32512      * Sends this dialog to the back (under) of any other visible dialogs
32513      * @return {Roo.BasicDialog} this
32514      */
32515     toBack : function(){
32516         Roo.DialogManager.sendToBack(this);
32517         return this;
32518     },
32519
32520     /**
32521      * Centers this dialog in the viewport
32522      * @return {Roo.BasicDialog} this
32523      */
32524     center : function(){
32525         var xy = this.el.getCenterXY(true);
32526         this.moveTo(xy[0], xy[1]);
32527         return this;
32528     },
32529
32530     /**
32531      * Moves the dialog's top-left corner to the specified point
32532      * @param {Number} x
32533      * @param {Number} y
32534      * @return {Roo.BasicDialog} this
32535      */
32536     moveTo : function(x, y){
32537         this.xy = [x,y];
32538         if(this.isVisible()){
32539             this.el.setXY(this.xy);
32540             this.adjustAssets();
32541         }
32542         return this;
32543     },
32544
32545     /**
32546      * Aligns the dialog to the specified element
32547      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32548      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32549      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32550      * @return {Roo.BasicDialog} this
32551      */
32552     alignTo : function(element, position, offsets){
32553         this.xy = this.el.getAlignToXY(element, position, offsets);
32554         if(this.isVisible()){
32555             this.el.setXY(this.xy);
32556             this.adjustAssets();
32557         }
32558         return this;
32559     },
32560
32561     /**
32562      * Anchors an element to another element and realigns it when the window is resized.
32563      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32564      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32565      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32566      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32567      * is a number, it is used as the buffer delay (defaults to 50ms).
32568      * @return {Roo.BasicDialog} this
32569      */
32570     anchorTo : function(el, alignment, offsets, monitorScroll){
32571         var action = function(){
32572             this.alignTo(el, alignment, offsets);
32573         };
32574         Roo.EventManager.onWindowResize(action, this);
32575         var tm = typeof monitorScroll;
32576         if(tm != 'undefined'){
32577             Roo.EventManager.on(window, 'scroll', action, this,
32578                 {buffer: tm == 'number' ? monitorScroll : 50});
32579         }
32580         action.call(this);
32581         return this;
32582     },
32583
32584     /**
32585      * Returns true if the dialog is visible
32586      * @return {Boolean}
32587      */
32588     isVisible : function(){
32589         return this.el.isVisible();
32590     },
32591
32592     // private
32593     animHide : function(callback){
32594         var b = Roo.get(this.animateTarget).getBox();
32595         this.proxy.show();
32596         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32597         this.el.hide();
32598         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32599                     this.hideEl.createDelegate(this, [callback]));
32600     },
32601
32602     /**
32603      * Hides the dialog.
32604      * @param {Function} callback (optional) Function to call when the dialog is hidden
32605      * @return {Roo.BasicDialog} this
32606      */
32607     hide : function(callback){
32608         if (this.fireEvent("beforehide", this) === false){
32609             return;
32610         }
32611         if(this.shadow){
32612             this.shadow.hide();
32613         }
32614         if(this.shim) {
32615           this.shim.hide();
32616         }
32617         // sometimes animateTarget seems to get set.. causing problems...
32618         // this just double checks..
32619         if(this.animateTarget && Roo.get(this.animateTarget)) {
32620            this.animHide(callback);
32621         }else{
32622             this.el.hide();
32623             this.hideEl(callback);
32624         }
32625         return this;
32626     },
32627
32628     // private
32629     hideEl : function(callback){
32630         this.proxy.hide();
32631         if(this.modal){
32632             this.mask.hide();
32633             Roo.get(document.body).removeClass("x-body-masked");
32634         }
32635         this.fireEvent("hide", this);
32636         if(typeof callback == "function"){
32637             callback();
32638         }
32639     },
32640
32641     // private
32642     hideAction : function(){
32643         this.setLeft("-10000px");
32644         this.setTop("-10000px");
32645         this.setStyle("visibility", "hidden");
32646     },
32647
32648     // private
32649     refreshSize : function(){
32650         this.size = this.el.getSize();
32651         this.xy = this.el.getXY();
32652         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32653     },
32654
32655     // private
32656     // z-index is managed by the DialogManager and may be overwritten at any time
32657     setZIndex : function(index){
32658         if(this.modal){
32659             this.mask.setStyle("z-index", index);
32660         }
32661         if(this.shim){
32662             this.shim.setStyle("z-index", ++index);
32663         }
32664         if(this.shadow){
32665             this.shadow.setZIndex(++index);
32666         }
32667         this.el.setStyle("z-index", ++index);
32668         if(this.proxy){
32669             this.proxy.setStyle("z-index", ++index);
32670         }
32671         if(this.resizer){
32672             this.resizer.proxy.setStyle("z-index", ++index);
32673         }
32674
32675         this.lastZIndex = index;
32676     },
32677
32678     /**
32679      * Returns the element for this dialog
32680      * @return {Roo.Element} The underlying dialog Element
32681      */
32682     getEl : function(){
32683         return this.el;
32684     }
32685 });
32686
32687 /**
32688  * @class Roo.DialogManager
32689  * Provides global access to BasicDialogs that have been created and
32690  * support for z-indexing (layering) multiple open dialogs.
32691  */
32692 Roo.DialogManager = function(){
32693     var list = {};
32694     var accessList = [];
32695     var front = null;
32696
32697     // private
32698     var sortDialogs = function(d1, d2){
32699         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32700     };
32701
32702     // private
32703     var orderDialogs = function(){
32704         accessList.sort(sortDialogs);
32705         var seed = Roo.DialogManager.zseed;
32706         for(var i = 0, len = accessList.length; i < len; i++){
32707             var dlg = accessList[i];
32708             if(dlg){
32709                 dlg.setZIndex(seed + (i*10));
32710             }
32711         }
32712     };
32713
32714     return {
32715         /**
32716          * The starting z-index for BasicDialogs (defaults to 9000)
32717          * @type Number The z-index value
32718          */
32719         zseed : 9000,
32720
32721         // private
32722         register : function(dlg){
32723             list[dlg.id] = dlg;
32724             accessList.push(dlg);
32725         },
32726
32727         // private
32728         unregister : function(dlg){
32729             delete list[dlg.id];
32730             var i=0;
32731             var len=0;
32732             if(!accessList.indexOf){
32733                 for(  i = 0, len = accessList.length; i < len; i++){
32734                     if(accessList[i] == dlg){
32735                         accessList.splice(i, 1);
32736                         return;
32737                     }
32738                 }
32739             }else{
32740                  i = accessList.indexOf(dlg);
32741                 if(i != -1){
32742                     accessList.splice(i, 1);
32743                 }
32744             }
32745         },
32746
32747         /**
32748          * Gets a registered dialog by id
32749          * @param {String/Object} id The id of the dialog or a dialog
32750          * @return {Roo.BasicDialog} this
32751          */
32752         get : function(id){
32753             return typeof id == "object" ? id : list[id];
32754         },
32755
32756         /**
32757          * Brings the specified dialog to the front
32758          * @param {String/Object} dlg The id of the dialog or a dialog
32759          * @return {Roo.BasicDialog} this
32760          */
32761         bringToFront : function(dlg){
32762             dlg = this.get(dlg);
32763             if(dlg != front){
32764                 front = dlg;
32765                 dlg._lastAccess = new Date().getTime();
32766                 orderDialogs();
32767             }
32768             return dlg;
32769         },
32770
32771         /**
32772          * Sends the specified dialog to the back
32773          * @param {String/Object} dlg The id of the dialog or a dialog
32774          * @return {Roo.BasicDialog} this
32775          */
32776         sendToBack : function(dlg){
32777             dlg = this.get(dlg);
32778             dlg._lastAccess = -(new Date().getTime());
32779             orderDialogs();
32780             return dlg;
32781         },
32782
32783         /**
32784          * Hides all dialogs
32785          */
32786         hideAll : function(){
32787             for(var id in list){
32788                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32789                     list[id].hide();
32790                 }
32791             }
32792         }
32793     };
32794 }();
32795
32796 /**
32797  * @class Roo.LayoutDialog
32798  * @extends Roo.BasicDialog
32799  * Dialog which provides adjustments for working with a layout in a Dialog.
32800  * Add your necessary layout config options to the dialog's config.<br>
32801  * Example usage (including a nested layout):
32802  * <pre><code>
32803 if(!dialog){
32804     dialog = new Roo.LayoutDialog("download-dlg", {
32805         modal: true,
32806         width:600,
32807         height:450,
32808         shadow:true,
32809         minWidth:500,
32810         minHeight:350,
32811         autoTabs:true,
32812         proxyDrag:true,
32813         // layout config merges with the dialog config
32814         center:{
32815             tabPosition: "top",
32816             alwaysShowTabs: true
32817         }
32818     });
32819     dialog.addKeyListener(27, dialog.hide, dialog);
32820     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32821     dialog.addButton("Build It!", this.getDownload, this);
32822
32823     // we can even add nested layouts
32824     var innerLayout = new Roo.BorderLayout("dl-inner", {
32825         east: {
32826             initialSize: 200,
32827             autoScroll:true,
32828             split:true
32829         },
32830         center: {
32831             autoScroll:true
32832         }
32833     });
32834     innerLayout.beginUpdate();
32835     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32836     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32837     innerLayout.endUpdate(true);
32838
32839     var layout = dialog.getLayout();
32840     layout.beginUpdate();
32841     layout.add("center", new Roo.ContentPanel("standard-panel",
32842                         {title: "Download the Source", fitToFrame:true}));
32843     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32844                {title: "Build your own roo.js"}));
32845     layout.getRegion("center").showPanel(sp);
32846     layout.endUpdate();
32847 }
32848 </code></pre>
32849     * @constructor
32850     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32851     * @param {Object} config configuration options
32852   */
32853 Roo.LayoutDialog = function(el, cfg){
32854     
32855     var config=  cfg;
32856     if (typeof(cfg) == 'undefined') {
32857         config = Roo.apply({}, el);
32858         // not sure why we use documentElement here.. - it should always be body.
32859         // IE7 borks horribly if we use documentElement.
32860         // webkit also does not like documentElement - it creates a body element...
32861         el = Roo.get( document.body || document.documentElement ).createChild();
32862         //config.autoCreate = true;
32863     }
32864     
32865     
32866     config.autoTabs = false;
32867     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32868     this.body.setStyle({overflow:"hidden", position:"relative"});
32869     this.layout = new Roo.BorderLayout(this.body.dom, config);
32870     this.layout.monitorWindowResize = false;
32871     this.el.addClass("x-dlg-auto-layout");
32872     // fix case when center region overwrites center function
32873     this.center = Roo.BasicDialog.prototype.center;
32874     this.on("show", this.layout.layout, this.layout, true);
32875     if (config.items) {
32876         var xitems = config.items;
32877         delete config.items;
32878         Roo.each(xitems, this.addxtype, this);
32879     }
32880     
32881     
32882 };
32883 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32884     /**
32885      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32886      * @deprecated
32887      */
32888     endUpdate : function(){
32889         this.layout.endUpdate();
32890     },
32891
32892     /**
32893      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32894      *  @deprecated
32895      */
32896     beginUpdate : function(){
32897         this.layout.beginUpdate();
32898     },
32899
32900     /**
32901      * Get the BorderLayout for this dialog
32902      * @return {Roo.BorderLayout}
32903      */
32904     getLayout : function(){
32905         return this.layout;
32906     },
32907
32908     showEl : function(){
32909         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32910         if(Roo.isIE7){
32911             this.layout.layout();
32912         }
32913     },
32914
32915     // private
32916     // Use the syncHeightBeforeShow config option to control this automatically
32917     syncBodyHeight : function(){
32918         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32919         if(this.layout){this.layout.layout();}
32920     },
32921     
32922       /**
32923      * Add an xtype element (actually adds to the layout.)
32924      * @return {Object} xdata xtype object data.
32925      */
32926     
32927     addxtype : function(c) {
32928         return this.layout.addxtype(c);
32929     }
32930 });/*
32931  * Based on:
32932  * Ext JS Library 1.1.1
32933  * Copyright(c) 2006-2007, Ext JS, LLC.
32934  *
32935  * Originally Released Under LGPL - original licence link has changed is not relivant.
32936  *
32937  * Fork - LGPL
32938  * <script type="text/javascript">
32939  */
32940  
32941 /**
32942  * @class Roo.MessageBox
32943  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32944  * Example usage:
32945  *<pre><code>
32946 // Basic alert:
32947 Roo.Msg.alert('Status', 'Changes saved successfully.');
32948
32949 // Prompt for user data:
32950 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32951     if (btn == 'ok'){
32952         // process text value...
32953     }
32954 });
32955
32956 // Show a dialog using config options:
32957 Roo.Msg.show({
32958    title:'Save Changes?',
32959    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32960    buttons: Roo.Msg.YESNOCANCEL,
32961    fn: processResult,
32962    animEl: 'elId'
32963 });
32964 </code></pre>
32965  * @singleton
32966  */
32967 Roo.MessageBox = function(){
32968     var dlg, opt, mask, waitTimer;
32969     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
32970     var buttons, activeTextEl, bwidth;
32971
32972     // private
32973     var handleButton = function(button){
32974         dlg.hide();
32975         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
32976     };
32977
32978     // private
32979     var handleHide = function(){
32980         if(opt && opt.cls){
32981             dlg.el.removeClass(opt.cls);
32982         }
32983         if(waitTimer){
32984             Roo.TaskMgr.stop(waitTimer);
32985             waitTimer = null;
32986         }
32987     };
32988
32989     // private
32990     var updateButtons = function(b){
32991         var width = 0;
32992         if(!b){
32993             buttons["ok"].hide();
32994             buttons["cancel"].hide();
32995             buttons["yes"].hide();
32996             buttons["no"].hide();
32997             dlg.footer.dom.style.display = 'none';
32998             return width;
32999         }
33000         dlg.footer.dom.style.display = '';
33001         for(var k in buttons){
33002             if(typeof buttons[k] != "function"){
33003                 if(b[k]){
33004                     buttons[k].show();
33005                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33006                     width += buttons[k].el.getWidth()+15;
33007                 }else{
33008                     buttons[k].hide();
33009                 }
33010             }
33011         }
33012         return width;
33013     };
33014
33015     // private
33016     var handleEsc = function(d, k, e){
33017         if(opt && opt.closable !== false){
33018             dlg.hide();
33019         }
33020         if(e){
33021             e.stopEvent();
33022         }
33023     };
33024
33025     return {
33026         /**
33027          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33028          * @return {Roo.BasicDialog} The BasicDialog element
33029          */
33030         getDialog : function(){
33031            if(!dlg){
33032                 dlg = new Roo.BasicDialog("x-msg-box", {
33033                     autoCreate : true,
33034                     shadow: true,
33035                     draggable: true,
33036                     resizable:false,
33037                     constraintoviewport:false,
33038                     fixedcenter:true,
33039                     collapsible : false,
33040                     shim:true,
33041                     modal: true,
33042                     width:400, height:100,
33043                     buttonAlign:"center",
33044                     closeClick : function(){
33045                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33046                             handleButton("no");
33047                         }else{
33048                             handleButton("cancel");
33049                         }
33050                     }
33051                 });
33052                 dlg.on("hide", handleHide);
33053                 mask = dlg.mask;
33054                 dlg.addKeyListener(27, handleEsc);
33055                 buttons = {};
33056                 var bt = this.buttonText;
33057                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33058                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33059                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33060                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33061                 bodyEl = dlg.body.createChild({
33062
33063                     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>'
33064                 });
33065                 msgEl = bodyEl.dom.firstChild;
33066                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33067                 textboxEl.enableDisplayMode();
33068                 textboxEl.addKeyListener([10,13], function(){
33069                     if(dlg.isVisible() && opt && opt.buttons){
33070                         if(opt.buttons.ok){
33071                             handleButton("ok");
33072                         }else if(opt.buttons.yes){
33073                             handleButton("yes");
33074                         }
33075                     }
33076                 });
33077                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33078                 textareaEl.enableDisplayMode();
33079                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33080                 progressEl.enableDisplayMode();
33081                 var pf = progressEl.dom.firstChild;
33082                 if (pf) {
33083                     pp = Roo.get(pf.firstChild);
33084                     pp.setHeight(pf.offsetHeight);
33085                 }
33086                 
33087             }
33088             return dlg;
33089         },
33090
33091         /**
33092          * Updates the message box body text
33093          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33094          * the XHTML-compliant non-breaking space character '&amp;#160;')
33095          * @return {Roo.MessageBox} This message box
33096          */
33097         updateText : function(text){
33098             if(!dlg.isVisible() && !opt.width){
33099                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33100             }
33101             msgEl.innerHTML = text || '&#160;';
33102       
33103             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33104             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33105             var w = Math.max(
33106                     Math.min(opt.width || cw , this.maxWidth), 
33107                     Math.max(opt.minWidth || this.minWidth, bwidth)
33108             );
33109             if(opt.prompt){
33110                 activeTextEl.setWidth(w);
33111             }
33112             if(dlg.isVisible()){
33113                 dlg.fixedcenter = false;
33114             }
33115             // to big, make it scroll. = But as usual stupid IE does not support
33116             // !important..
33117             
33118             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33119                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33120                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33121             } else {
33122                 bodyEl.dom.style.height = '';
33123                 bodyEl.dom.style.overflowY = '';
33124             }
33125             if (cw > w) {
33126                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33127             } else {
33128                 bodyEl.dom.style.overflowX = '';
33129             }
33130             
33131             dlg.setContentSize(w, bodyEl.getHeight());
33132             if(dlg.isVisible()){
33133                 dlg.fixedcenter = true;
33134             }
33135             return this;
33136         },
33137
33138         /**
33139          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33140          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33141          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33142          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33143          * @return {Roo.MessageBox} This message box
33144          */
33145         updateProgress : function(value, text){
33146             if(text){
33147                 this.updateText(text);
33148             }
33149             if (pp) { // weird bug on my firefox - for some reason this is not defined
33150                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33151             }
33152             return this;
33153         },        
33154
33155         /**
33156          * Returns true if the message box is currently displayed
33157          * @return {Boolean} True if the message box is visible, else false
33158          */
33159         isVisible : function(){
33160             return dlg && dlg.isVisible();  
33161         },
33162
33163         /**
33164          * Hides the message box if it is displayed
33165          */
33166         hide : function(){
33167             if(this.isVisible()){
33168                 dlg.hide();
33169             }  
33170         },
33171
33172         /**
33173          * Displays a new message box, or reinitializes an existing message box, based on the config options
33174          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33175          * The following config object properties are supported:
33176          * <pre>
33177 Property    Type             Description
33178 ----------  ---------------  ------------------------------------------------------------------------------------
33179 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33180                                    closes (defaults to undefined)
33181 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33182                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33183 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33184                                    progress and wait dialogs will ignore this property and always hide the
33185                                    close button as they can only be closed programmatically.
33186 cls               String           A custom CSS class to apply to the message box element
33187 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33188                                    displayed (defaults to 75)
33189 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33190                                    function will be btn (the name of the button that was clicked, if applicable,
33191                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33192                                    Progress and wait dialogs will ignore this option since they do not respond to
33193                                    user actions and can only be closed programmatically, so any required function
33194                                    should be called by the same code after it closes the dialog.
33195 icon              String           A CSS class that provides a background image to be used as an icon for
33196                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33197 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33198 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33199 modal             Boolean          False to allow user interaction with the page while the message box is
33200                                    displayed (defaults to true)
33201 msg               String           A string that will replace the existing message box body text (defaults
33202                                    to the XHTML-compliant non-breaking space character '&#160;')
33203 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33204 progress          Boolean          True to display a progress bar (defaults to false)
33205 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33206 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33207 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33208 title             String           The title text
33209 value             String           The string value to set into the active textbox element if displayed
33210 wait              Boolean          True to display a progress bar (defaults to false)
33211 width             Number           The width of the dialog in pixels
33212 </pre>
33213          *
33214          * Example usage:
33215          * <pre><code>
33216 Roo.Msg.show({
33217    title: 'Address',
33218    msg: 'Please enter your address:',
33219    width: 300,
33220    buttons: Roo.MessageBox.OKCANCEL,
33221    multiline: true,
33222    fn: saveAddress,
33223    animEl: 'addAddressBtn'
33224 });
33225 </code></pre>
33226          * @param {Object} config Configuration options
33227          * @return {Roo.MessageBox} This message box
33228          */
33229         show : function(options)
33230         {
33231             
33232             // this causes nightmares if you show one dialog after another
33233             // especially on callbacks..
33234              
33235             if(this.isVisible()){
33236                 
33237                 this.hide();
33238                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33239                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33240                 Roo.log("New Dialog Message:" +  options.msg )
33241                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33242                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33243                 
33244             }
33245             var d = this.getDialog();
33246             opt = options;
33247             d.setTitle(opt.title || "&#160;");
33248             d.close.setDisplayed(opt.closable !== false);
33249             activeTextEl = textboxEl;
33250             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33251             if(opt.prompt){
33252                 if(opt.multiline){
33253                     textboxEl.hide();
33254                     textareaEl.show();
33255                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33256                         opt.multiline : this.defaultTextHeight);
33257                     activeTextEl = textareaEl;
33258                 }else{
33259                     textboxEl.show();
33260                     textareaEl.hide();
33261                 }
33262             }else{
33263                 textboxEl.hide();
33264                 textareaEl.hide();
33265             }
33266             progressEl.setDisplayed(opt.progress === true);
33267             this.updateProgress(0);
33268             activeTextEl.dom.value = opt.value || "";
33269             if(opt.prompt){
33270                 dlg.setDefaultButton(activeTextEl);
33271             }else{
33272                 var bs = opt.buttons;
33273                 var db = null;
33274                 if(bs && bs.ok){
33275                     db = buttons["ok"];
33276                 }else if(bs && bs.yes){
33277                     db = buttons["yes"];
33278                 }
33279                 dlg.setDefaultButton(db);
33280             }
33281             bwidth = updateButtons(opt.buttons);
33282             this.updateText(opt.msg);
33283             if(opt.cls){
33284                 d.el.addClass(opt.cls);
33285             }
33286             d.proxyDrag = opt.proxyDrag === true;
33287             d.modal = opt.modal !== false;
33288             d.mask = opt.modal !== false ? mask : false;
33289             if(!d.isVisible()){
33290                 // force it to the end of the z-index stack so it gets a cursor in FF
33291                 document.body.appendChild(dlg.el.dom);
33292                 d.animateTarget = null;
33293                 d.show(options.animEl);
33294             }
33295             return this;
33296         },
33297
33298         /**
33299          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33300          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33301          * and closing the message box when the process is complete.
33302          * @param {String} title The title bar text
33303          * @param {String} msg The message box body text
33304          * @return {Roo.MessageBox} This message box
33305          */
33306         progress : function(title, msg){
33307             this.show({
33308                 title : title,
33309                 msg : msg,
33310                 buttons: false,
33311                 progress:true,
33312                 closable:false,
33313                 minWidth: this.minProgressWidth,
33314                 modal : true
33315             });
33316             return this;
33317         },
33318
33319         /**
33320          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33321          * If a callback function is passed it will be called after the user clicks the button, and the
33322          * id of the button that was clicked will be passed as the only parameter to the callback
33323          * (could also be the top-right close button).
33324          * @param {String} title The title bar text
33325          * @param {String} msg The message box body text
33326          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33327          * @param {Object} scope (optional) The scope of the callback function
33328          * @return {Roo.MessageBox} This message box
33329          */
33330         alert : function(title, msg, fn, scope){
33331             this.show({
33332                 title : title,
33333                 msg : msg,
33334                 buttons: this.OK,
33335                 fn: fn,
33336                 scope : scope,
33337                 modal : true
33338             });
33339             return this;
33340         },
33341
33342         /**
33343          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33344          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33345          * You are responsible for closing the message box when the process is complete.
33346          * @param {String} msg The message box body text
33347          * @param {String} title (optional) The title bar text
33348          * @return {Roo.MessageBox} This message box
33349          */
33350         wait : function(msg, title){
33351             this.show({
33352                 title : title,
33353                 msg : msg,
33354                 buttons: false,
33355                 closable:false,
33356                 progress:true,
33357                 modal:true,
33358                 width:300,
33359                 wait:true
33360             });
33361             waitTimer = Roo.TaskMgr.start({
33362                 run: function(i){
33363                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33364                 },
33365                 interval: 1000
33366             });
33367             return this;
33368         },
33369
33370         /**
33371          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33372          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33373          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33374          * @param {String} title The title bar text
33375          * @param {String} msg The message box body text
33376          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33377          * @param {Object} scope (optional) The scope of the callback function
33378          * @return {Roo.MessageBox} This message box
33379          */
33380         confirm : function(title, msg, fn, scope){
33381             this.show({
33382                 title : title,
33383                 msg : msg,
33384                 buttons: this.YESNO,
33385                 fn: fn,
33386                 scope : scope,
33387                 modal : true
33388             });
33389             return this;
33390         },
33391
33392         /**
33393          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33394          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33395          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33396          * (could also be the top-right close button) and the text that was entered will be passed as the two
33397          * parameters to the callback.
33398          * @param {String} title The title bar text
33399          * @param {String} msg The message box body text
33400          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33401          * @param {Object} scope (optional) The scope of the callback function
33402          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33403          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33404          * @return {Roo.MessageBox} This message box
33405          */
33406         prompt : function(title, msg, fn, scope, multiline){
33407             this.show({
33408                 title : title,
33409                 msg : msg,
33410                 buttons: this.OKCANCEL,
33411                 fn: fn,
33412                 minWidth:250,
33413                 scope : scope,
33414                 prompt:true,
33415                 multiline: multiline,
33416                 modal : true
33417             });
33418             return this;
33419         },
33420
33421         /**
33422          * Button config that displays a single OK button
33423          * @type Object
33424          */
33425         OK : {ok:true},
33426         /**
33427          * Button config that displays Yes and No buttons
33428          * @type Object
33429          */
33430         YESNO : {yes:true, no:true},
33431         /**
33432          * Button config that displays OK and Cancel buttons
33433          * @type Object
33434          */
33435         OKCANCEL : {ok:true, cancel:true},
33436         /**
33437          * Button config that displays Yes, No and Cancel buttons
33438          * @type Object
33439          */
33440         YESNOCANCEL : {yes:true, no:true, cancel:true},
33441
33442         /**
33443          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33444          * @type Number
33445          */
33446         defaultTextHeight : 75,
33447         /**
33448          * The maximum width in pixels of the message box (defaults to 600)
33449          * @type Number
33450          */
33451         maxWidth : 600,
33452         /**
33453          * The minimum width in pixels of the message box (defaults to 100)
33454          * @type Number
33455          */
33456         minWidth : 100,
33457         /**
33458          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33459          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33460          * @type Number
33461          */
33462         minProgressWidth : 250,
33463         /**
33464          * An object containing the default button text strings that can be overriden for localized language support.
33465          * Supported properties are: ok, cancel, yes and no.
33466          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33467          * @type Object
33468          */
33469         buttonText : {
33470             ok : "OK",
33471             cancel : "Cancel",
33472             yes : "Yes",
33473             no : "No"
33474         }
33475     };
33476 }();
33477
33478 /**
33479  * Shorthand for {@link Roo.MessageBox}
33480  */
33481 Roo.Msg = Roo.MessageBox;/*
33482  * Based on:
33483  * Ext JS Library 1.1.1
33484  * Copyright(c) 2006-2007, Ext JS, LLC.
33485  *
33486  * Originally Released Under LGPL - original licence link has changed is not relivant.
33487  *
33488  * Fork - LGPL
33489  * <script type="text/javascript">
33490  */
33491 /**
33492  * @class Roo.QuickTips
33493  * Provides attractive and customizable tooltips for any element.
33494  * @singleton
33495  */
33496 Roo.QuickTips = function(){
33497     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33498     var ce, bd, xy, dd;
33499     var visible = false, disabled = true, inited = false;
33500     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33501     
33502     var onOver = function(e){
33503         if(disabled){
33504             return;
33505         }
33506         var t = e.getTarget();
33507         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33508             return;
33509         }
33510         if(ce && t == ce.el){
33511             clearTimeout(hideProc);
33512             return;
33513         }
33514         if(t && tagEls[t.id]){
33515             tagEls[t.id].el = t;
33516             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33517             return;
33518         }
33519         var ttp, et = Roo.fly(t);
33520         var ns = cfg.namespace;
33521         if(tm.interceptTitles && t.title){
33522             ttp = t.title;
33523             t.qtip = ttp;
33524             t.removeAttribute("title");
33525             e.preventDefault();
33526         }else{
33527             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33528         }
33529         if(ttp){
33530             showProc = show.defer(tm.showDelay, tm, [{
33531                 el: t, 
33532                 text: ttp, 
33533                 width: et.getAttributeNS(ns, cfg.width),
33534                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33535                 title: et.getAttributeNS(ns, cfg.title),
33536                     cls: et.getAttributeNS(ns, cfg.cls)
33537             }]);
33538         }
33539     };
33540     
33541     var onOut = function(e){
33542         clearTimeout(showProc);
33543         var t = e.getTarget();
33544         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33545             hideProc = setTimeout(hide, tm.hideDelay);
33546         }
33547     };
33548     
33549     var onMove = function(e){
33550         if(disabled){
33551             return;
33552         }
33553         xy = e.getXY();
33554         xy[1] += 18;
33555         if(tm.trackMouse && ce){
33556             el.setXY(xy);
33557         }
33558     };
33559     
33560     var onDown = function(e){
33561         clearTimeout(showProc);
33562         clearTimeout(hideProc);
33563         if(!e.within(el)){
33564             if(tm.hideOnClick){
33565                 hide();
33566                 tm.disable();
33567                 tm.enable.defer(100, tm);
33568             }
33569         }
33570     };
33571     
33572     var getPad = function(){
33573         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33574     };
33575
33576     var show = function(o){
33577         if(disabled){
33578             return;
33579         }
33580         clearTimeout(dismissProc);
33581         ce = o;
33582         if(removeCls){ // in case manually hidden
33583             el.removeClass(removeCls);
33584             removeCls = null;
33585         }
33586         if(ce.cls){
33587             el.addClass(ce.cls);
33588             removeCls = ce.cls;
33589         }
33590         if(ce.title){
33591             tipTitle.update(ce.title);
33592             tipTitle.show();
33593         }else{
33594             tipTitle.update('');
33595             tipTitle.hide();
33596         }
33597         el.dom.style.width  = tm.maxWidth+'px';
33598         //tipBody.dom.style.width = '';
33599         tipBodyText.update(o.text);
33600         var p = getPad(), w = ce.width;
33601         if(!w){
33602             var td = tipBodyText.dom;
33603             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33604             if(aw > tm.maxWidth){
33605                 w = tm.maxWidth;
33606             }else if(aw < tm.minWidth){
33607                 w = tm.minWidth;
33608             }else{
33609                 w = aw;
33610             }
33611         }
33612         //tipBody.setWidth(w);
33613         el.setWidth(parseInt(w, 10) + p);
33614         if(ce.autoHide === false){
33615             close.setDisplayed(true);
33616             if(dd){
33617                 dd.unlock();
33618             }
33619         }else{
33620             close.setDisplayed(false);
33621             if(dd){
33622                 dd.lock();
33623             }
33624         }
33625         if(xy){
33626             el.avoidY = xy[1]-18;
33627             el.setXY(xy);
33628         }
33629         if(tm.animate){
33630             el.setOpacity(.1);
33631             el.setStyle("visibility", "visible");
33632             el.fadeIn({callback: afterShow});
33633         }else{
33634             afterShow();
33635         }
33636     };
33637     
33638     var afterShow = function(){
33639         if(ce){
33640             el.show();
33641             esc.enable();
33642             if(tm.autoDismiss && ce.autoHide !== false){
33643                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33644             }
33645         }
33646     };
33647     
33648     var hide = function(noanim){
33649         clearTimeout(dismissProc);
33650         clearTimeout(hideProc);
33651         ce = null;
33652         if(el.isVisible()){
33653             esc.disable();
33654             if(noanim !== true && tm.animate){
33655                 el.fadeOut({callback: afterHide});
33656             }else{
33657                 afterHide();
33658             } 
33659         }
33660     };
33661     
33662     var afterHide = function(){
33663         el.hide();
33664         if(removeCls){
33665             el.removeClass(removeCls);
33666             removeCls = null;
33667         }
33668     };
33669     
33670     return {
33671         /**
33672         * @cfg {Number} minWidth
33673         * The minimum width of the quick tip (defaults to 40)
33674         */
33675        minWidth : 40,
33676         /**
33677         * @cfg {Number} maxWidth
33678         * The maximum width of the quick tip (defaults to 300)
33679         */
33680        maxWidth : 300,
33681         /**
33682         * @cfg {Boolean} interceptTitles
33683         * True to automatically use the element's DOM title value if available (defaults to false)
33684         */
33685        interceptTitles : false,
33686         /**
33687         * @cfg {Boolean} trackMouse
33688         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33689         */
33690        trackMouse : false,
33691         /**
33692         * @cfg {Boolean} hideOnClick
33693         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33694         */
33695        hideOnClick : true,
33696         /**
33697         * @cfg {Number} showDelay
33698         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33699         */
33700        showDelay : 500,
33701         /**
33702         * @cfg {Number} hideDelay
33703         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33704         */
33705        hideDelay : 200,
33706         /**
33707         * @cfg {Boolean} autoHide
33708         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33709         * Used in conjunction with hideDelay.
33710         */
33711        autoHide : true,
33712         /**
33713         * @cfg {Boolean}
33714         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33715         * (defaults to true).  Used in conjunction with autoDismissDelay.
33716         */
33717        autoDismiss : true,
33718         /**
33719         * @cfg {Number}
33720         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33721         */
33722        autoDismissDelay : 5000,
33723        /**
33724         * @cfg {Boolean} animate
33725         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33726         */
33727        animate : false,
33728
33729        /**
33730         * @cfg {String} title
33731         * Title text to display (defaults to '').  This can be any valid HTML markup.
33732         */
33733         title: '',
33734        /**
33735         * @cfg {String} text
33736         * Body text to display (defaults to '').  This can be any valid HTML markup.
33737         */
33738         text : '',
33739        /**
33740         * @cfg {String} cls
33741         * A CSS class to apply to the base quick tip element (defaults to '').
33742         */
33743         cls : '',
33744        /**
33745         * @cfg {Number} width
33746         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33747         * minWidth or maxWidth.
33748         */
33749         width : null,
33750
33751     /**
33752      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33753      * or display QuickTips in a page.
33754      */
33755        init : function(){
33756           tm = Roo.QuickTips;
33757           cfg = tm.tagConfig;
33758           if(!inited){
33759               if(!Roo.isReady){ // allow calling of init() before onReady
33760                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33761                   return;
33762               }
33763               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33764               el.fxDefaults = {stopFx: true};
33765               // maximum custom styling
33766               //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>');
33767               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>');              
33768               tipTitle = el.child('h3');
33769               tipTitle.enableDisplayMode("block");
33770               tipBody = el.child('div.x-tip-bd');
33771               tipBodyText = el.child('div.x-tip-bd-inner');
33772               //bdLeft = el.child('div.x-tip-bd-left');
33773               //bdRight = el.child('div.x-tip-bd-right');
33774               close = el.child('div.x-tip-close');
33775               close.enableDisplayMode("block");
33776               close.on("click", hide);
33777               var d = Roo.get(document);
33778               d.on("mousedown", onDown);
33779               d.on("mouseover", onOver);
33780               d.on("mouseout", onOut);
33781               d.on("mousemove", onMove);
33782               esc = d.addKeyListener(27, hide);
33783               esc.disable();
33784               if(Roo.dd.DD){
33785                   dd = el.initDD("default", null, {
33786                       onDrag : function(){
33787                           el.sync();  
33788                       }
33789                   });
33790                   dd.setHandleElId(tipTitle.id);
33791                   dd.lock();
33792               }
33793               inited = true;
33794           }
33795           this.enable(); 
33796        },
33797
33798     /**
33799      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33800      * are supported:
33801      * <pre>
33802 Property    Type                   Description
33803 ----------  ---------------------  ------------------------------------------------------------------------
33804 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33805      * </ul>
33806      * @param {Object} config The config object
33807      */
33808        register : function(config){
33809            var cs = config instanceof Array ? config : arguments;
33810            for(var i = 0, len = cs.length; i < len; i++) {
33811                var c = cs[i];
33812                var target = c.target;
33813                if(target){
33814                    if(target instanceof Array){
33815                        for(var j = 0, jlen = target.length; j < jlen; j++){
33816                            tagEls[target[j]] = c;
33817                        }
33818                    }else{
33819                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33820                    }
33821                }
33822            }
33823        },
33824
33825     /**
33826      * Removes this quick tip from its element and destroys it.
33827      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33828      */
33829        unregister : function(el){
33830            delete tagEls[Roo.id(el)];
33831        },
33832
33833     /**
33834      * Enable this quick tip.
33835      */
33836        enable : function(){
33837            if(inited && disabled){
33838                locks.pop();
33839                if(locks.length < 1){
33840                    disabled = false;
33841                }
33842            }
33843        },
33844
33845     /**
33846      * Disable this quick tip.
33847      */
33848        disable : function(){
33849           disabled = true;
33850           clearTimeout(showProc);
33851           clearTimeout(hideProc);
33852           clearTimeout(dismissProc);
33853           if(ce){
33854               hide(true);
33855           }
33856           locks.push(1);
33857        },
33858
33859     /**
33860      * Returns true if the quick tip is enabled, else false.
33861      */
33862        isEnabled : function(){
33863             return !disabled;
33864        },
33865
33866         // private
33867        tagConfig : {
33868            namespace : "roo", // was ext?? this may break..
33869            alt_namespace : "ext",
33870            attribute : "qtip",
33871            width : "width",
33872            target : "target",
33873            title : "qtitle",
33874            hide : "hide",
33875            cls : "qclass"
33876        }
33877    };
33878 }();
33879
33880 // backwards compat
33881 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33882  * Based on:
33883  * Ext JS Library 1.1.1
33884  * Copyright(c) 2006-2007, Ext JS, LLC.
33885  *
33886  * Originally Released Under LGPL - original licence link has changed is not relivant.
33887  *
33888  * Fork - LGPL
33889  * <script type="text/javascript">
33890  */
33891  
33892
33893 /**
33894  * @class Roo.tree.TreePanel
33895  * @extends Roo.data.Tree
33896
33897  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33898  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33899  * @cfg {Boolean} enableDD true to enable drag and drop
33900  * @cfg {Boolean} enableDrag true to enable just drag
33901  * @cfg {Boolean} enableDrop true to enable just drop
33902  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33903  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33904  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33905  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33906  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33907  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33908  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33909  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33910  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33911  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33912  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33913  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33914  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33915  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33916  * @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>
33917  * @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>
33918  * 
33919  * @constructor
33920  * @param {String/HTMLElement/Element} el The container element
33921  * @param {Object} config
33922  */
33923 Roo.tree.TreePanel = function(el, config){
33924     var root = false;
33925     var loader = false;
33926     if (config.root) {
33927         root = config.root;
33928         delete config.root;
33929     }
33930     if (config.loader) {
33931         loader = config.loader;
33932         delete config.loader;
33933     }
33934     
33935     Roo.apply(this, config);
33936     Roo.tree.TreePanel.superclass.constructor.call(this);
33937     this.el = Roo.get(el);
33938     this.el.addClass('x-tree');
33939     //console.log(root);
33940     if (root) {
33941         this.setRootNode( Roo.factory(root, Roo.tree));
33942     }
33943     if (loader) {
33944         this.loader = Roo.factory(loader, Roo.tree);
33945     }
33946    /**
33947     * Read-only. The id of the container element becomes this TreePanel's id.
33948     */
33949     this.id = this.el.id;
33950     this.addEvents({
33951         /**
33952         * @event beforeload
33953         * Fires before a node is loaded, return false to cancel
33954         * @param {Node} node The node being loaded
33955         */
33956         "beforeload" : true,
33957         /**
33958         * @event load
33959         * Fires when a node is loaded
33960         * @param {Node} node The node that was loaded
33961         */
33962         "load" : true,
33963         /**
33964         * @event textchange
33965         * Fires when the text for a node is changed
33966         * @param {Node} node The node
33967         * @param {String} text The new text
33968         * @param {String} oldText The old text
33969         */
33970         "textchange" : true,
33971         /**
33972         * @event beforeexpand
33973         * Fires before a node is expanded, return false to cancel.
33974         * @param {Node} node The node
33975         * @param {Boolean} deep
33976         * @param {Boolean} anim
33977         */
33978         "beforeexpand" : true,
33979         /**
33980         * @event beforecollapse
33981         * Fires before a node is collapsed, return false to cancel.
33982         * @param {Node} node The node
33983         * @param {Boolean} deep
33984         * @param {Boolean} anim
33985         */
33986         "beforecollapse" : true,
33987         /**
33988         * @event expand
33989         * Fires when a node is expanded
33990         * @param {Node} node The node
33991         */
33992         "expand" : true,
33993         /**
33994         * @event disabledchange
33995         * Fires when the disabled status of a node changes
33996         * @param {Node} node The node
33997         * @param {Boolean} disabled
33998         */
33999         "disabledchange" : true,
34000         /**
34001         * @event collapse
34002         * Fires when a node is collapsed
34003         * @param {Node} node The node
34004         */
34005         "collapse" : true,
34006         /**
34007         * @event beforeclick
34008         * Fires before click processing on a node. Return false to cancel the default action.
34009         * @param {Node} node The node
34010         * @param {Roo.EventObject} e The event object
34011         */
34012         "beforeclick":true,
34013         /**
34014         * @event checkchange
34015         * Fires when a node with a checkbox's checked property changes
34016         * @param {Node} this This node
34017         * @param {Boolean} checked
34018         */
34019         "checkchange":true,
34020         /**
34021         * @event click
34022         * Fires when a node is clicked
34023         * @param {Node} node The node
34024         * @param {Roo.EventObject} e The event object
34025         */
34026         "click":true,
34027         /**
34028         * @event dblclick
34029         * Fires when a node is double clicked
34030         * @param {Node} node The node
34031         * @param {Roo.EventObject} e The event object
34032         */
34033         "dblclick":true,
34034         /**
34035         * @event contextmenu
34036         * Fires when a node is right clicked
34037         * @param {Node} node The node
34038         * @param {Roo.EventObject} e The event object
34039         */
34040         "contextmenu":true,
34041         /**
34042         * @event beforechildrenrendered
34043         * Fires right before the child nodes for a node are rendered
34044         * @param {Node} node The node
34045         */
34046         "beforechildrenrendered":true,
34047         /**
34048         * @event startdrag
34049         * Fires when a node starts being dragged
34050         * @param {Roo.tree.TreePanel} this
34051         * @param {Roo.tree.TreeNode} node
34052         * @param {event} e The raw browser event
34053         */ 
34054        "startdrag" : true,
34055        /**
34056         * @event enddrag
34057         * Fires when a drag operation is complete
34058         * @param {Roo.tree.TreePanel} this
34059         * @param {Roo.tree.TreeNode} node
34060         * @param {event} e The raw browser event
34061         */
34062        "enddrag" : true,
34063        /**
34064         * @event dragdrop
34065         * Fires when a dragged node is dropped on a valid DD target
34066         * @param {Roo.tree.TreePanel} this
34067         * @param {Roo.tree.TreeNode} node
34068         * @param {DD} dd The dd it was dropped on
34069         * @param {event} e The raw browser event
34070         */
34071        "dragdrop" : true,
34072        /**
34073         * @event beforenodedrop
34074         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34075         * passed to handlers has the following properties:<br />
34076         * <ul style="padding:5px;padding-left:16px;">
34077         * <li>tree - The TreePanel</li>
34078         * <li>target - The node being targeted for the drop</li>
34079         * <li>data - The drag data from the drag source</li>
34080         * <li>point - The point of the drop - append, above or below</li>
34081         * <li>source - The drag source</li>
34082         * <li>rawEvent - Raw mouse event</li>
34083         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34084         * to be inserted by setting them on this object.</li>
34085         * <li>cancel - Set this to true to cancel the drop.</li>
34086         * </ul>
34087         * @param {Object} dropEvent
34088         */
34089        "beforenodedrop" : true,
34090        /**
34091         * @event nodedrop
34092         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34093         * passed to handlers has the following properties:<br />
34094         * <ul style="padding:5px;padding-left:16px;">
34095         * <li>tree - The TreePanel</li>
34096         * <li>target - The node being targeted for the drop</li>
34097         * <li>data - The drag data from the drag source</li>
34098         * <li>point - The point of the drop - append, above or below</li>
34099         * <li>source - The drag source</li>
34100         * <li>rawEvent - Raw mouse event</li>
34101         * <li>dropNode - Dropped node(s).</li>
34102         * </ul>
34103         * @param {Object} dropEvent
34104         */
34105        "nodedrop" : true,
34106         /**
34107         * @event nodedragover
34108         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34109         * passed to handlers has the following properties:<br />
34110         * <ul style="padding:5px;padding-left:16px;">
34111         * <li>tree - The TreePanel</li>
34112         * <li>target - The node being targeted for the drop</li>
34113         * <li>data - The drag data from the drag source</li>
34114         * <li>point - The point of the drop - append, above or below</li>
34115         * <li>source - The drag source</li>
34116         * <li>rawEvent - Raw mouse event</li>
34117         * <li>dropNode - Drop node(s) provided by the source.</li>
34118         * <li>cancel - Set this to true to signal drop not allowed.</li>
34119         * </ul>
34120         * @param {Object} dragOverEvent
34121         */
34122        "nodedragover" : true
34123         
34124     });
34125     if(this.singleExpand){
34126        this.on("beforeexpand", this.restrictExpand, this);
34127     }
34128     if (this.editor) {
34129         this.editor.tree = this;
34130         this.editor = Roo.factory(this.editor, Roo.tree);
34131     }
34132     
34133     if (this.selModel) {
34134         this.selModel = Roo.factory(this.selModel, Roo.tree);
34135     }
34136    
34137 };
34138 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34139     rootVisible : true,
34140     animate: Roo.enableFx,
34141     lines : true,
34142     enableDD : false,
34143     hlDrop : Roo.enableFx,
34144   
34145     renderer: false,
34146     
34147     rendererTip: false,
34148     // private
34149     restrictExpand : function(node){
34150         var p = node.parentNode;
34151         if(p){
34152             if(p.expandedChild && p.expandedChild.parentNode == p){
34153                 p.expandedChild.collapse();
34154             }
34155             p.expandedChild = node;
34156         }
34157     },
34158
34159     // private override
34160     setRootNode : function(node){
34161         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34162         if(!this.rootVisible){
34163             node.ui = new Roo.tree.RootTreeNodeUI(node);
34164         }
34165         return node;
34166     },
34167
34168     /**
34169      * Returns the container element for this TreePanel
34170      */
34171     getEl : function(){
34172         return this.el;
34173     },
34174
34175     /**
34176      * Returns the default TreeLoader for this TreePanel
34177      */
34178     getLoader : function(){
34179         return this.loader;
34180     },
34181
34182     /**
34183      * Expand all nodes
34184      */
34185     expandAll : function(){
34186         this.root.expand(true);
34187     },
34188
34189     /**
34190      * Collapse all nodes
34191      */
34192     collapseAll : function(){
34193         this.root.collapse(true);
34194     },
34195
34196     /**
34197      * Returns the selection model used by this TreePanel
34198      */
34199     getSelectionModel : function(){
34200         if(!this.selModel){
34201             this.selModel = new Roo.tree.DefaultSelectionModel();
34202         }
34203         return this.selModel;
34204     },
34205
34206     /**
34207      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34208      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34209      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34210      * @return {Array}
34211      */
34212     getChecked : function(a, startNode){
34213         startNode = startNode || this.root;
34214         var r = [];
34215         var f = function(){
34216             if(this.attributes.checked){
34217                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34218             }
34219         }
34220         startNode.cascade(f);
34221         return r;
34222     },
34223
34224     /**
34225      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34226      * @param {String} path
34227      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34228      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34229      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34230      */
34231     expandPath : function(path, attr, callback){
34232         attr = attr || "id";
34233         var keys = path.split(this.pathSeparator);
34234         var curNode = this.root;
34235         if(curNode.attributes[attr] != keys[1]){ // invalid root
34236             if(callback){
34237                 callback(false, null);
34238             }
34239             return;
34240         }
34241         var index = 1;
34242         var f = function(){
34243             if(++index == keys.length){
34244                 if(callback){
34245                     callback(true, curNode);
34246                 }
34247                 return;
34248             }
34249             var c = curNode.findChild(attr, keys[index]);
34250             if(!c){
34251                 if(callback){
34252                     callback(false, curNode);
34253                 }
34254                 return;
34255             }
34256             curNode = c;
34257             c.expand(false, false, f);
34258         };
34259         curNode.expand(false, false, f);
34260     },
34261
34262     /**
34263      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34264      * @param {String} path
34265      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34266      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34267      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34268      */
34269     selectPath : function(path, attr, callback){
34270         attr = attr || "id";
34271         var keys = path.split(this.pathSeparator);
34272         var v = keys.pop();
34273         if(keys.length > 0){
34274             var f = function(success, node){
34275                 if(success && node){
34276                     var n = node.findChild(attr, v);
34277                     if(n){
34278                         n.select();
34279                         if(callback){
34280                             callback(true, n);
34281                         }
34282                     }else if(callback){
34283                         callback(false, n);
34284                     }
34285                 }else{
34286                     if(callback){
34287                         callback(false, n);
34288                     }
34289                 }
34290             };
34291             this.expandPath(keys.join(this.pathSeparator), attr, f);
34292         }else{
34293             this.root.select();
34294             if(callback){
34295                 callback(true, this.root);
34296             }
34297         }
34298     },
34299
34300     getTreeEl : function(){
34301         return this.el;
34302     },
34303
34304     /**
34305      * Trigger rendering of this TreePanel
34306      */
34307     render : function(){
34308         if (this.innerCt) {
34309             return this; // stop it rendering more than once!!
34310         }
34311         
34312         this.innerCt = this.el.createChild({tag:"ul",
34313                cls:"x-tree-root-ct " +
34314                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34315
34316         if(this.containerScroll){
34317             Roo.dd.ScrollManager.register(this.el);
34318         }
34319         if((this.enableDD || this.enableDrop) && !this.dropZone){
34320            /**
34321             * The dropZone used by this tree if drop is enabled
34322             * @type Roo.tree.TreeDropZone
34323             */
34324              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34325                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34326            });
34327         }
34328         if((this.enableDD || this.enableDrag) && !this.dragZone){
34329            /**
34330             * The dragZone used by this tree if drag is enabled
34331             * @type Roo.tree.TreeDragZone
34332             */
34333             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34334                ddGroup: this.ddGroup || "TreeDD",
34335                scroll: this.ddScroll
34336            });
34337         }
34338         this.getSelectionModel().init(this);
34339         if (!this.root) {
34340             Roo.log("ROOT not set in tree");
34341             return this;
34342         }
34343         this.root.render();
34344         if(!this.rootVisible){
34345             this.root.renderChildren();
34346         }
34347         return this;
34348     }
34349 });/*
34350  * Based on:
34351  * Ext JS Library 1.1.1
34352  * Copyright(c) 2006-2007, Ext JS, LLC.
34353  *
34354  * Originally Released Under LGPL - original licence link has changed is not relivant.
34355  *
34356  * Fork - LGPL
34357  * <script type="text/javascript">
34358  */
34359  
34360
34361 /**
34362  * @class Roo.tree.DefaultSelectionModel
34363  * @extends Roo.util.Observable
34364  * The default single selection for a TreePanel.
34365  * @param {Object} cfg Configuration
34366  */
34367 Roo.tree.DefaultSelectionModel = function(cfg){
34368    this.selNode = null;
34369    
34370    
34371    
34372    this.addEvents({
34373        /**
34374         * @event selectionchange
34375         * Fires when the selected node changes
34376         * @param {DefaultSelectionModel} this
34377         * @param {TreeNode} node the new selection
34378         */
34379        "selectionchange" : true,
34380
34381        /**
34382         * @event beforeselect
34383         * Fires before the selected node changes, return false to cancel the change
34384         * @param {DefaultSelectionModel} this
34385         * @param {TreeNode} node the new selection
34386         * @param {TreeNode} node the old selection
34387         */
34388        "beforeselect" : true
34389    });
34390    
34391     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34392 };
34393
34394 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34395     init : function(tree){
34396         this.tree = tree;
34397         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34398         tree.on("click", this.onNodeClick, this);
34399     },
34400     
34401     onNodeClick : function(node, e){
34402         if (e.ctrlKey && this.selNode == node)  {
34403             this.unselect(node);
34404             return;
34405         }
34406         this.select(node);
34407     },
34408     
34409     /**
34410      * Select a node.
34411      * @param {TreeNode} node The node to select
34412      * @return {TreeNode} The selected node
34413      */
34414     select : function(node){
34415         var last = this.selNode;
34416         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34417             if(last){
34418                 last.ui.onSelectedChange(false);
34419             }
34420             this.selNode = node;
34421             node.ui.onSelectedChange(true);
34422             this.fireEvent("selectionchange", this, node, last);
34423         }
34424         return node;
34425     },
34426     
34427     /**
34428      * Deselect a node.
34429      * @param {TreeNode} node The node to unselect
34430      */
34431     unselect : function(node){
34432         if(this.selNode == node){
34433             this.clearSelections();
34434         }    
34435     },
34436     
34437     /**
34438      * Clear all selections
34439      */
34440     clearSelections : function(){
34441         var n = this.selNode;
34442         if(n){
34443             n.ui.onSelectedChange(false);
34444             this.selNode = null;
34445             this.fireEvent("selectionchange", this, null);
34446         }
34447         return n;
34448     },
34449     
34450     /**
34451      * Get the selected node
34452      * @return {TreeNode} The selected node
34453      */
34454     getSelectedNode : function(){
34455         return this.selNode;    
34456     },
34457     
34458     /**
34459      * Returns true if the node is selected
34460      * @param {TreeNode} node The node to check
34461      * @return {Boolean}
34462      */
34463     isSelected : function(node){
34464         return this.selNode == node;  
34465     },
34466
34467     /**
34468      * Selects the node above the selected node in the tree, intelligently walking the nodes
34469      * @return TreeNode The new selection
34470      */
34471     selectPrevious : function(){
34472         var s = this.selNode || this.lastSelNode;
34473         if(!s){
34474             return null;
34475         }
34476         var ps = s.previousSibling;
34477         if(ps){
34478             if(!ps.isExpanded() || ps.childNodes.length < 1){
34479                 return this.select(ps);
34480             } else{
34481                 var lc = ps.lastChild;
34482                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34483                     lc = lc.lastChild;
34484                 }
34485                 return this.select(lc);
34486             }
34487         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34488             return this.select(s.parentNode);
34489         }
34490         return null;
34491     },
34492
34493     /**
34494      * Selects the node above the selected node in the tree, intelligently walking the nodes
34495      * @return TreeNode The new selection
34496      */
34497     selectNext : function(){
34498         var s = this.selNode || this.lastSelNode;
34499         if(!s){
34500             return null;
34501         }
34502         if(s.firstChild && s.isExpanded()){
34503              return this.select(s.firstChild);
34504          }else if(s.nextSibling){
34505              return this.select(s.nextSibling);
34506          }else if(s.parentNode){
34507             var newS = null;
34508             s.parentNode.bubble(function(){
34509                 if(this.nextSibling){
34510                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34511                     return false;
34512                 }
34513             });
34514             return newS;
34515          }
34516         return null;
34517     },
34518
34519     onKeyDown : function(e){
34520         var s = this.selNode || this.lastSelNode;
34521         // undesirable, but required
34522         var sm = this;
34523         if(!s){
34524             return;
34525         }
34526         var k = e.getKey();
34527         switch(k){
34528              case e.DOWN:
34529                  e.stopEvent();
34530                  this.selectNext();
34531              break;
34532              case e.UP:
34533                  e.stopEvent();
34534                  this.selectPrevious();
34535              break;
34536              case e.RIGHT:
34537                  e.preventDefault();
34538                  if(s.hasChildNodes()){
34539                      if(!s.isExpanded()){
34540                          s.expand();
34541                      }else if(s.firstChild){
34542                          this.select(s.firstChild, e);
34543                      }
34544                  }
34545              break;
34546              case e.LEFT:
34547                  e.preventDefault();
34548                  if(s.hasChildNodes() && s.isExpanded()){
34549                      s.collapse();
34550                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34551                      this.select(s.parentNode, e);
34552                  }
34553              break;
34554         };
34555     }
34556 });
34557
34558 /**
34559  * @class Roo.tree.MultiSelectionModel
34560  * @extends Roo.util.Observable
34561  * Multi selection for a TreePanel.
34562  * @param {Object} cfg Configuration
34563  */
34564 Roo.tree.MultiSelectionModel = function(){
34565    this.selNodes = [];
34566    this.selMap = {};
34567    this.addEvents({
34568        /**
34569         * @event selectionchange
34570         * Fires when the selected nodes change
34571         * @param {MultiSelectionModel} this
34572         * @param {Array} nodes Array of the selected nodes
34573         */
34574        "selectionchange" : true
34575    });
34576    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34577    
34578 };
34579
34580 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34581     init : function(tree){
34582         this.tree = tree;
34583         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34584         tree.on("click", this.onNodeClick, this);
34585     },
34586     
34587     onNodeClick : function(node, e){
34588         this.select(node, e, e.ctrlKey);
34589     },
34590     
34591     /**
34592      * Select a node.
34593      * @param {TreeNode} node The node to select
34594      * @param {EventObject} e (optional) An event associated with the selection
34595      * @param {Boolean} keepExisting True to retain existing selections
34596      * @return {TreeNode} The selected node
34597      */
34598     select : function(node, e, keepExisting){
34599         if(keepExisting !== true){
34600             this.clearSelections(true);
34601         }
34602         if(this.isSelected(node)){
34603             this.lastSelNode = node;
34604             return node;
34605         }
34606         this.selNodes.push(node);
34607         this.selMap[node.id] = node;
34608         this.lastSelNode = node;
34609         node.ui.onSelectedChange(true);
34610         this.fireEvent("selectionchange", this, this.selNodes);
34611         return node;
34612     },
34613     
34614     /**
34615      * Deselect a node.
34616      * @param {TreeNode} node The node to unselect
34617      */
34618     unselect : function(node){
34619         if(this.selMap[node.id]){
34620             node.ui.onSelectedChange(false);
34621             var sn = this.selNodes;
34622             var index = -1;
34623             if(sn.indexOf){
34624                 index = sn.indexOf(node);
34625             }else{
34626                 for(var i = 0, len = sn.length; i < len; i++){
34627                     if(sn[i] == node){
34628                         index = i;
34629                         break;
34630                     }
34631                 }
34632             }
34633             if(index != -1){
34634                 this.selNodes.splice(index, 1);
34635             }
34636             delete this.selMap[node.id];
34637             this.fireEvent("selectionchange", this, this.selNodes);
34638         }
34639     },
34640     
34641     /**
34642      * Clear all selections
34643      */
34644     clearSelections : function(suppressEvent){
34645         var sn = this.selNodes;
34646         if(sn.length > 0){
34647             for(var i = 0, len = sn.length; i < len; i++){
34648                 sn[i].ui.onSelectedChange(false);
34649             }
34650             this.selNodes = [];
34651             this.selMap = {};
34652             if(suppressEvent !== true){
34653                 this.fireEvent("selectionchange", this, this.selNodes);
34654             }
34655         }
34656     },
34657     
34658     /**
34659      * Returns true if the node is selected
34660      * @param {TreeNode} node The node to check
34661      * @return {Boolean}
34662      */
34663     isSelected : function(node){
34664         return this.selMap[node.id] ? true : false;  
34665     },
34666     
34667     /**
34668      * Returns an array of the selected nodes
34669      * @return {Array}
34670      */
34671     getSelectedNodes : function(){
34672         return this.selNodes;    
34673     },
34674
34675     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34676
34677     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34678
34679     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34680 });/*
34681  * Based on:
34682  * Ext JS Library 1.1.1
34683  * Copyright(c) 2006-2007, Ext JS, LLC.
34684  *
34685  * Originally Released Under LGPL - original licence link has changed is not relivant.
34686  *
34687  * Fork - LGPL
34688  * <script type="text/javascript">
34689  */
34690  
34691 /**
34692  * @class Roo.tree.TreeNode
34693  * @extends Roo.data.Node
34694  * @cfg {String} text The text for this node
34695  * @cfg {Boolean} expanded true to start the node expanded
34696  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34697  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34698  * @cfg {Boolean} disabled true to start the node disabled
34699  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34700  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34701  * @cfg {String} cls A css class to be added to the node
34702  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34703  * @cfg {String} href URL of the link used for the node (defaults to #)
34704  * @cfg {String} hrefTarget target frame for the link
34705  * @cfg {String} qtip An Ext QuickTip for the node
34706  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34707  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34708  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34709  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34710  * (defaults to undefined with no checkbox rendered)
34711  * @constructor
34712  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34713  */
34714 Roo.tree.TreeNode = function(attributes){
34715     attributes = attributes || {};
34716     if(typeof attributes == "string"){
34717         attributes = {text: attributes};
34718     }
34719     this.childrenRendered = false;
34720     this.rendered = false;
34721     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34722     this.expanded = attributes.expanded === true;
34723     this.isTarget = attributes.isTarget !== false;
34724     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34725     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34726
34727     /**
34728      * Read-only. The text for this node. To change it use setText().
34729      * @type String
34730      */
34731     this.text = attributes.text;
34732     /**
34733      * True if this node is disabled.
34734      * @type Boolean
34735      */
34736     this.disabled = attributes.disabled === true;
34737
34738     this.addEvents({
34739         /**
34740         * @event textchange
34741         * Fires when the text for this node is changed
34742         * @param {Node} this This node
34743         * @param {String} text The new text
34744         * @param {String} oldText The old text
34745         */
34746         "textchange" : true,
34747         /**
34748         * @event beforeexpand
34749         * Fires before this node is expanded, return false to cancel.
34750         * @param {Node} this This node
34751         * @param {Boolean} deep
34752         * @param {Boolean} anim
34753         */
34754         "beforeexpand" : true,
34755         /**
34756         * @event beforecollapse
34757         * Fires before this node is collapsed, return false to cancel.
34758         * @param {Node} this This node
34759         * @param {Boolean} deep
34760         * @param {Boolean} anim
34761         */
34762         "beforecollapse" : true,
34763         /**
34764         * @event expand
34765         * Fires when this node is expanded
34766         * @param {Node} this This node
34767         */
34768         "expand" : true,
34769         /**
34770         * @event disabledchange
34771         * Fires when the disabled status of this node changes
34772         * @param {Node} this This node
34773         * @param {Boolean} disabled
34774         */
34775         "disabledchange" : true,
34776         /**
34777         * @event collapse
34778         * Fires when this node is collapsed
34779         * @param {Node} this This node
34780         */
34781         "collapse" : true,
34782         /**
34783         * @event beforeclick
34784         * Fires before click processing. Return false to cancel the default action.
34785         * @param {Node} this This node
34786         * @param {Roo.EventObject} e The event object
34787         */
34788         "beforeclick":true,
34789         /**
34790         * @event checkchange
34791         * Fires when a node with a checkbox's checked property changes
34792         * @param {Node} this This node
34793         * @param {Boolean} checked
34794         */
34795         "checkchange":true,
34796         /**
34797         * @event click
34798         * Fires when this node is clicked
34799         * @param {Node} this This node
34800         * @param {Roo.EventObject} e The event object
34801         */
34802         "click":true,
34803         /**
34804         * @event dblclick
34805         * Fires when this node is double clicked
34806         * @param {Node} this This node
34807         * @param {Roo.EventObject} e The event object
34808         */
34809         "dblclick":true,
34810         /**
34811         * @event contextmenu
34812         * Fires when this node is right clicked
34813         * @param {Node} this This node
34814         * @param {Roo.EventObject} e The event object
34815         */
34816         "contextmenu":true,
34817         /**
34818         * @event beforechildrenrendered
34819         * Fires right before the child nodes for this node are rendered
34820         * @param {Node} this This node
34821         */
34822         "beforechildrenrendered":true
34823     });
34824
34825     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34826
34827     /**
34828      * Read-only. The UI for this node
34829      * @type TreeNodeUI
34830      */
34831     this.ui = new uiClass(this);
34832     
34833     // finally support items[]
34834     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34835         return;
34836     }
34837     
34838     
34839     Roo.each(this.attributes.items, function(c) {
34840         this.appendChild(Roo.factory(c,Roo.Tree));
34841     }, this);
34842     delete this.attributes.items;
34843     
34844     
34845     
34846 };
34847 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34848     preventHScroll: true,
34849     /**
34850      * Returns true if this node is expanded
34851      * @return {Boolean}
34852      */
34853     isExpanded : function(){
34854         return this.expanded;
34855     },
34856
34857     /**
34858      * Returns the UI object for this node
34859      * @return {TreeNodeUI}
34860      */
34861     getUI : function(){
34862         return this.ui;
34863     },
34864
34865     // private override
34866     setFirstChild : function(node){
34867         var of = this.firstChild;
34868         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34869         if(this.childrenRendered && of && node != of){
34870             of.renderIndent(true, true);
34871         }
34872         if(this.rendered){
34873             this.renderIndent(true, true);
34874         }
34875     },
34876
34877     // private override
34878     setLastChild : function(node){
34879         var ol = this.lastChild;
34880         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34881         if(this.childrenRendered && ol && node != ol){
34882             ol.renderIndent(true, true);
34883         }
34884         if(this.rendered){
34885             this.renderIndent(true, true);
34886         }
34887     },
34888
34889     // these methods are overridden to provide lazy rendering support
34890     // private override
34891     appendChild : function()
34892     {
34893         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34894         if(node && this.childrenRendered){
34895             node.render();
34896         }
34897         this.ui.updateExpandIcon();
34898         return node;
34899     },
34900
34901     // private override
34902     removeChild : function(node){
34903         this.ownerTree.getSelectionModel().unselect(node);
34904         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34905         // if it's been rendered remove dom node
34906         if(this.childrenRendered){
34907             node.ui.remove();
34908         }
34909         if(this.childNodes.length < 1){
34910             this.collapse(false, false);
34911         }else{
34912             this.ui.updateExpandIcon();
34913         }
34914         if(!this.firstChild) {
34915             this.childrenRendered = false;
34916         }
34917         return node;
34918     },
34919
34920     // private override
34921     insertBefore : function(node, refNode){
34922         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34923         if(newNode && refNode && this.childrenRendered){
34924             node.render();
34925         }
34926         this.ui.updateExpandIcon();
34927         return newNode;
34928     },
34929
34930     /**
34931      * Sets the text for this node
34932      * @param {String} text
34933      */
34934     setText : function(text){
34935         var oldText = this.text;
34936         this.text = text;
34937         this.attributes.text = text;
34938         if(this.rendered){ // event without subscribing
34939             this.ui.onTextChange(this, text, oldText);
34940         }
34941         this.fireEvent("textchange", this, text, oldText);
34942     },
34943
34944     /**
34945      * Triggers selection of this node
34946      */
34947     select : function(){
34948         this.getOwnerTree().getSelectionModel().select(this);
34949     },
34950
34951     /**
34952      * Triggers deselection of this node
34953      */
34954     unselect : function(){
34955         this.getOwnerTree().getSelectionModel().unselect(this);
34956     },
34957
34958     /**
34959      * Returns true if this node is selected
34960      * @return {Boolean}
34961      */
34962     isSelected : function(){
34963         return this.getOwnerTree().getSelectionModel().isSelected(this);
34964     },
34965
34966     /**
34967      * Expand this node.
34968      * @param {Boolean} deep (optional) True to expand all children as well
34969      * @param {Boolean} anim (optional) false to cancel the default animation
34970      * @param {Function} callback (optional) A callback to be called when
34971      * expanding this node completes (does not wait for deep expand to complete).
34972      * Called with 1 parameter, this node.
34973      */
34974     expand : function(deep, anim, callback){
34975         if(!this.expanded){
34976             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
34977                 return;
34978             }
34979             if(!this.childrenRendered){
34980                 this.renderChildren();
34981             }
34982             this.expanded = true;
34983             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
34984                 this.ui.animExpand(function(){
34985                     this.fireEvent("expand", this);
34986                     if(typeof callback == "function"){
34987                         callback(this);
34988                     }
34989                     if(deep === true){
34990                         this.expandChildNodes(true);
34991                     }
34992                 }.createDelegate(this));
34993                 return;
34994             }else{
34995                 this.ui.expand();
34996                 this.fireEvent("expand", this);
34997                 if(typeof callback == "function"){
34998                     callback(this);
34999                 }
35000             }
35001         }else{
35002            if(typeof callback == "function"){
35003                callback(this);
35004            }
35005         }
35006         if(deep === true){
35007             this.expandChildNodes(true);
35008         }
35009     },
35010
35011     isHiddenRoot : function(){
35012         return this.isRoot && !this.getOwnerTree().rootVisible;
35013     },
35014
35015     /**
35016      * Collapse this node.
35017      * @param {Boolean} deep (optional) True to collapse all children as well
35018      * @param {Boolean} anim (optional) false to cancel the default animation
35019      */
35020     collapse : function(deep, anim){
35021         if(this.expanded && !this.isHiddenRoot()){
35022             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35023                 return;
35024             }
35025             this.expanded = false;
35026             if((this.getOwnerTree().animate && anim !== false) || anim){
35027                 this.ui.animCollapse(function(){
35028                     this.fireEvent("collapse", this);
35029                     if(deep === true){
35030                         this.collapseChildNodes(true);
35031                     }
35032                 }.createDelegate(this));
35033                 return;
35034             }else{
35035                 this.ui.collapse();
35036                 this.fireEvent("collapse", this);
35037             }
35038         }
35039         if(deep === true){
35040             var cs = this.childNodes;
35041             for(var i = 0, len = cs.length; i < len; i++) {
35042                 cs[i].collapse(true, false);
35043             }
35044         }
35045     },
35046
35047     // private
35048     delayedExpand : function(delay){
35049         if(!this.expandProcId){
35050             this.expandProcId = this.expand.defer(delay, this);
35051         }
35052     },
35053
35054     // private
35055     cancelExpand : function(){
35056         if(this.expandProcId){
35057             clearTimeout(this.expandProcId);
35058         }
35059         this.expandProcId = false;
35060     },
35061
35062     /**
35063      * Toggles expanded/collapsed state of the node
35064      */
35065     toggle : function(){
35066         if(this.expanded){
35067             this.collapse();
35068         }else{
35069             this.expand();
35070         }
35071     },
35072
35073     /**
35074      * Ensures all parent nodes are expanded
35075      */
35076     ensureVisible : function(callback){
35077         var tree = this.getOwnerTree();
35078         tree.expandPath(this.parentNode.getPath(), false, function(){
35079             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35080             Roo.callback(callback);
35081         }.createDelegate(this));
35082     },
35083
35084     /**
35085      * Expand all child nodes
35086      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35087      */
35088     expandChildNodes : function(deep){
35089         var cs = this.childNodes;
35090         for(var i = 0, len = cs.length; i < len; i++) {
35091                 cs[i].expand(deep);
35092         }
35093     },
35094
35095     /**
35096      * Collapse all child nodes
35097      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35098      */
35099     collapseChildNodes : function(deep){
35100         var cs = this.childNodes;
35101         for(var i = 0, len = cs.length; i < len; i++) {
35102                 cs[i].collapse(deep);
35103         }
35104     },
35105
35106     /**
35107      * Disables this node
35108      */
35109     disable : function(){
35110         this.disabled = true;
35111         this.unselect();
35112         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35113             this.ui.onDisableChange(this, true);
35114         }
35115         this.fireEvent("disabledchange", this, true);
35116     },
35117
35118     /**
35119      * Enables this node
35120      */
35121     enable : function(){
35122         this.disabled = false;
35123         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35124             this.ui.onDisableChange(this, false);
35125         }
35126         this.fireEvent("disabledchange", this, false);
35127     },
35128
35129     // private
35130     renderChildren : function(suppressEvent){
35131         if(suppressEvent !== false){
35132             this.fireEvent("beforechildrenrendered", this);
35133         }
35134         var cs = this.childNodes;
35135         for(var i = 0, len = cs.length; i < len; i++){
35136             cs[i].render(true);
35137         }
35138         this.childrenRendered = true;
35139     },
35140
35141     // private
35142     sort : function(fn, scope){
35143         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35144         if(this.childrenRendered){
35145             var cs = this.childNodes;
35146             for(var i = 0, len = cs.length; i < len; i++){
35147                 cs[i].render(true);
35148             }
35149         }
35150     },
35151
35152     // private
35153     render : function(bulkRender){
35154         this.ui.render(bulkRender);
35155         if(!this.rendered){
35156             this.rendered = true;
35157             if(this.expanded){
35158                 this.expanded = false;
35159                 this.expand(false, false);
35160             }
35161         }
35162     },
35163
35164     // private
35165     renderIndent : function(deep, refresh){
35166         if(refresh){
35167             this.ui.childIndent = null;
35168         }
35169         this.ui.renderIndent();
35170         if(deep === true && this.childrenRendered){
35171             var cs = this.childNodes;
35172             for(var i = 0, len = cs.length; i < len; i++){
35173                 cs[i].renderIndent(true, refresh);
35174             }
35175         }
35176     }
35177 });/*
35178  * Based on:
35179  * Ext JS Library 1.1.1
35180  * Copyright(c) 2006-2007, Ext JS, LLC.
35181  *
35182  * Originally Released Under LGPL - original licence link has changed is not relivant.
35183  *
35184  * Fork - LGPL
35185  * <script type="text/javascript">
35186  */
35187  
35188 /**
35189  * @class Roo.tree.AsyncTreeNode
35190  * @extends Roo.tree.TreeNode
35191  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35192  * @constructor
35193  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35194  */
35195  Roo.tree.AsyncTreeNode = function(config){
35196     this.loaded = false;
35197     this.loading = false;
35198     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35199     /**
35200     * @event beforeload
35201     * Fires before this node is loaded, return false to cancel
35202     * @param {Node} this This node
35203     */
35204     this.addEvents({'beforeload':true, 'load': true});
35205     /**
35206     * @event load
35207     * Fires when this node is loaded
35208     * @param {Node} this This node
35209     */
35210     /**
35211      * The loader used by this node (defaults to using the tree's defined loader)
35212      * @type TreeLoader
35213      * @property loader
35214      */
35215 };
35216 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35217     expand : function(deep, anim, callback){
35218         if(this.loading){ // if an async load is already running, waiting til it's done
35219             var timer;
35220             var f = function(){
35221                 if(!this.loading){ // done loading
35222                     clearInterval(timer);
35223                     this.expand(deep, anim, callback);
35224                 }
35225             }.createDelegate(this);
35226             timer = setInterval(f, 200);
35227             return;
35228         }
35229         if(!this.loaded){
35230             if(this.fireEvent("beforeload", this) === false){
35231                 return;
35232             }
35233             this.loading = true;
35234             this.ui.beforeLoad(this);
35235             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35236             if(loader){
35237                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35238                 return;
35239             }
35240         }
35241         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35242     },
35243     
35244     /**
35245      * Returns true if this node is currently loading
35246      * @return {Boolean}
35247      */
35248     isLoading : function(){
35249         return this.loading;  
35250     },
35251     
35252     loadComplete : function(deep, anim, callback){
35253         this.loading = false;
35254         this.loaded = true;
35255         this.ui.afterLoad(this);
35256         this.fireEvent("load", this);
35257         this.expand(deep, anim, callback);
35258     },
35259     
35260     /**
35261      * Returns true if this node has been loaded
35262      * @return {Boolean}
35263      */
35264     isLoaded : function(){
35265         return this.loaded;
35266     },
35267     
35268     hasChildNodes : function(){
35269         if(!this.isLeaf() && !this.loaded){
35270             return true;
35271         }else{
35272             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35273         }
35274     },
35275
35276     /**
35277      * Trigger a reload for this node
35278      * @param {Function} callback
35279      */
35280     reload : function(callback){
35281         this.collapse(false, false);
35282         while(this.firstChild){
35283             this.removeChild(this.firstChild);
35284         }
35285         this.childrenRendered = false;
35286         this.loaded = false;
35287         if(this.isHiddenRoot()){
35288             this.expanded = false;
35289         }
35290         this.expand(false, false, callback);
35291     }
35292 });/*
35293  * Based on:
35294  * Ext JS Library 1.1.1
35295  * Copyright(c) 2006-2007, Ext JS, LLC.
35296  *
35297  * Originally Released Under LGPL - original licence link has changed is not relivant.
35298  *
35299  * Fork - LGPL
35300  * <script type="text/javascript">
35301  */
35302  
35303 /**
35304  * @class Roo.tree.TreeNodeUI
35305  * @constructor
35306  * @param {Object} node The node to render
35307  * The TreeNode UI implementation is separate from the
35308  * tree implementation. Unless you are customizing the tree UI,
35309  * you should never have to use this directly.
35310  */
35311 Roo.tree.TreeNodeUI = function(node){
35312     this.node = node;
35313     this.rendered = false;
35314     this.animating = false;
35315     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35316 };
35317
35318 Roo.tree.TreeNodeUI.prototype = {
35319     removeChild : function(node){
35320         if(this.rendered){
35321             this.ctNode.removeChild(node.ui.getEl());
35322         }
35323     },
35324
35325     beforeLoad : function(){
35326          this.addClass("x-tree-node-loading");
35327     },
35328
35329     afterLoad : function(){
35330          this.removeClass("x-tree-node-loading");
35331     },
35332
35333     onTextChange : function(node, text, oldText){
35334         if(this.rendered){
35335             this.textNode.innerHTML = text;
35336         }
35337     },
35338
35339     onDisableChange : function(node, state){
35340         this.disabled = state;
35341         if(state){
35342             this.addClass("x-tree-node-disabled");
35343         }else{
35344             this.removeClass("x-tree-node-disabled");
35345         }
35346     },
35347
35348     onSelectedChange : function(state){
35349         if(state){
35350             this.focus();
35351             this.addClass("x-tree-selected");
35352         }else{
35353             //this.blur();
35354             this.removeClass("x-tree-selected");
35355         }
35356     },
35357
35358     onMove : function(tree, node, oldParent, newParent, index, refNode){
35359         this.childIndent = null;
35360         if(this.rendered){
35361             var targetNode = newParent.ui.getContainer();
35362             if(!targetNode){//target not rendered
35363                 this.holder = document.createElement("div");
35364                 this.holder.appendChild(this.wrap);
35365                 return;
35366             }
35367             var insertBefore = refNode ? refNode.ui.getEl() : null;
35368             if(insertBefore){
35369                 targetNode.insertBefore(this.wrap, insertBefore);
35370             }else{
35371                 targetNode.appendChild(this.wrap);
35372             }
35373             this.node.renderIndent(true);
35374         }
35375     },
35376
35377     addClass : function(cls){
35378         if(this.elNode){
35379             Roo.fly(this.elNode).addClass(cls);
35380         }
35381     },
35382
35383     removeClass : function(cls){
35384         if(this.elNode){
35385             Roo.fly(this.elNode).removeClass(cls);
35386         }
35387     },
35388
35389     remove : function(){
35390         if(this.rendered){
35391             this.holder = document.createElement("div");
35392             this.holder.appendChild(this.wrap);
35393         }
35394     },
35395
35396     fireEvent : function(){
35397         return this.node.fireEvent.apply(this.node, arguments);
35398     },
35399
35400     initEvents : function(){
35401         this.node.on("move", this.onMove, this);
35402         var E = Roo.EventManager;
35403         var a = this.anchor;
35404
35405         var el = Roo.fly(a, '_treeui');
35406
35407         if(Roo.isOpera){ // opera render bug ignores the CSS
35408             el.setStyle("text-decoration", "none");
35409         }
35410
35411         el.on("click", this.onClick, this);
35412         el.on("dblclick", this.onDblClick, this);
35413
35414         if(this.checkbox){
35415             Roo.EventManager.on(this.checkbox,
35416                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35417         }
35418
35419         el.on("contextmenu", this.onContextMenu, this);
35420
35421         var icon = Roo.fly(this.iconNode);
35422         icon.on("click", this.onClick, this);
35423         icon.on("dblclick", this.onDblClick, this);
35424         icon.on("contextmenu", this.onContextMenu, this);
35425         E.on(this.ecNode, "click", this.ecClick, this, true);
35426
35427         if(this.node.disabled){
35428             this.addClass("x-tree-node-disabled");
35429         }
35430         if(this.node.hidden){
35431             this.addClass("x-tree-node-disabled");
35432         }
35433         var ot = this.node.getOwnerTree();
35434         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35435         if(dd && (!this.node.isRoot || ot.rootVisible)){
35436             Roo.dd.Registry.register(this.elNode, {
35437                 node: this.node,
35438                 handles: this.getDDHandles(),
35439                 isHandle: false
35440             });
35441         }
35442     },
35443
35444     getDDHandles : function(){
35445         return [this.iconNode, this.textNode];
35446     },
35447
35448     hide : function(){
35449         if(this.rendered){
35450             this.wrap.style.display = "none";
35451         }
35452     },
35453
35454     show : function(){
35455         if(this.rendered){
35456             this.wrap.style.display = "";
35457         }
35458     },
35459
35460     onContextMenu : function(e){
35461         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35462             e.preventDefault();
35463             this.focus();
35464             this.fireEvent("contextmenu", this.node, e);
35465         }
35466     },
35467
35468     onClick : function(e){
35469         if(this.dropping){
35470             e.stopEvent();
35471             return;
35472         }
35473         if(this.fireEvent("beforeclick", this.node, e) !== false){
35474             if(!this.disabled && this.node.attributes.href){
35475                 this.fireEvent("click", this.node, e);
35476                 return;
35477             }
35478             e.preventDefault();
35479             if(this.disabled){
35480                 return;
35481             }
35482
35483             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35484                 this.node.toggle();
35485             }
35486
35487             this.fireEvent("click", this.node, e);
35488         }else{
35489             e.stopEvent();
35490         }
35491     },
35492
35493     onDblClick : function(e){
35494         e.preventDefault();
35495         if(this.disabled){
35496             return;
35497         }
35498         if(this.checkbox){
35499             this.toggleCheck();
35500         }
35501         if(!this.animating && this.node.hasChildNodes()){
35502             this.node.toggle();
35503         }
35504         this.fireEvent("dblclick", this.node, e);
35505     },
35506
35507     onCheckChange : function(){
35508         var checked = this.checkbox.checked;
35509         this.node.attributes.checked = checked;
35510         this.fireEvent('checkchange', this.node, checked);
35511     },
35512
35513     ecClick : function(e){
35514         if(!this.animating && this.node.hasChildNodes()){
35515             this.node.toggle();
35516         }
35517     },
35518
35519     startDrop : function(){
35520         this.dropping = true;
35521     },
35522
35523     // delayed drop so the click event doesn't get fired on a drop
35524     endDrop : function(){
35525        setTimeout(function(){
35526            this.dropping = false;
35527        }.createDelegate(this), 50);
35528     },
35529
35530     expand : function(){
35531         this.updateExpandIcon();
35532         this.ctNode.style.display = "";
35533     },
35534
35535     focus : function(){
35536         if(!this.node.preventHScroll){
35537             try{this.anchor.focus();
35538             }catch(e){}
35539         }else if(!Roo.isIE){
35540             try{
35541                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35542                 var l = noscroll.scrollLeft;
35543                 this.anchor.focus();
35544                 noscroll.scrollLeft = l;
35545             }catch(e){}
35546         }
35547     },
35548
35549     toggleCheck : function(value){
35550         var cb = this.checkbox;
35551         if(cb){
35552             cb.checked = (value === undefined ? !cb.checked : value);
35553         }
35554     },
35555
35556     blur : function(){
35557         try{
35558             this.anchor.blur();
35559         }catch(e){}
35560     },
35561
35562     animExpand : function(callback){
35563         var ct = Roo.get(this.ctNode);
35564         ct.stopFx();
35565         if(!this.node.hasChildNodes()){
35566             this.updateExpandIcon();
35567             this.ctNode.style.display = "";
35568             Roo.callback(callback);
35569             return;
35570         }
35571         this.animating = true;
35572         this.updateExpandIcon();
35573
35574         ct.slideIn('t', {
35575            callback : function(){
35576                this.animating = false;
35577                Roo.callback(callback);
35578             },
35579             scope: this,
35580             duration: this.node.ownerTree.duration || .25
35581         });
35582     },
35583
35584     highlight : function(){
35585         var tree = this.node.getOwnerTree();
35586         Roo.fly(this.wrap).highlight(
35587             tree.hlColor || "C3DAF9",
35588             {endColor: tree.hlBaseColor}
35589         );
35590     },
35591
35592     collapse : function(){
35593         this.updateExpandIcon();
35594         this.ctNode.style.display = "none";
35595     },
35596
35597     animCollapse : function(callback){
35598         var ct = Roo.get(this.ctNode);
35599         ct.enableDisplayMode('block');
35600         ct.stopFx();
35601
35602         this.animating = true;
35603         this.updateExpandIcon();
35604
35605         ct.slideOut('t', {
35606             callback : function(){
35607                this.animating = false;
35608                Roo.callback(callback);
35609             },
35610             scope: this,
35611             duration: this.node.ownerTree.duration || .25
35612         });
35613     },
35614
35615     getContainer : function(){
35616         return this.ctNode;
35617     },
35618
35619     getEl : function(){
35620         return this.wrap;
35621     },
35622
35623     appendDDGhost : function(ghostNode){
35624         ghostNode.appendChild(this.elNode.cloneNode(true));
35625     },
35626
35627     getDDRepairXY : function(){
35628         return Roo.lib.Dom.getXY(this.iconNode);
35629     },
35630
35631     onRender : function(){
35632         this.render();
35633     },
35634
35635     render : function(bulkRender){
35636         var n = this.node, a = n.attributes;
35637         var targetNode = n.parentNode ?
35638               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35639
35640         if(!this.rendered){
35641             this.rendered = true;
35642
35643             this.renderElements(n, a, targetNode, bulkRender);
35644
35645             if(a.qtip){
35646                if(this.textNode.setAttributeNS){
35647                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35648                    if(a.qtipTitle){
35649                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35650                    }
35651                }else{
35652                    this.textNode.setAttribute("ext:qtip", a.qtip);
35653                    if(a.qtipTitle){
35654                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35655                    }
35656                }
35657             }else if(a.qtipCfg){
35658                 a.qtipCfg.target = Roo.id(this.textNode);
35659                 Roo.QuickTips.register(a.qtipCfg);
35660             }
35661             this.initEvents();
35662             if(!this.node.expanded){
35663                 this.updateExpandIcon();
35664             }
35665         }else{
35666             if(bulkRender === true) {
35667                 targetNode.appendChild(this.wrap);
35668             }
35669         }
35670     },
35671
35672     renderElements : function(n, a, targetNode, bulkRender)
35673     {
35674         // add some indent caching, this helps performance when rendering a large tree
35675         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35676         var t = n.getOwnerTree();
35677         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35678         if (typeof(n.attributes.html) != 'undefined') {
35679             txt = n.attributes.html;
35680         }
35681         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35682         var cb = typeof a.checked == 'boolean';
35683         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35684         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35685             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35686             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35687             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35688             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35689             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35690              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35691                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35692             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35693             "</li>"];
35694
35695         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35696             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35697                                 n.nextSibling.ui.getEl(), buf.join(""));
35698         }else{
35699             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35700         }
35701
35702         this.elNode = this.wrap.childNodes[0];
35703         this.ctNode = this.wrap.childNodes[1];
35704         var cs = this.elNode.childNodes;
35705         this.indentNode = cs[0];
35706         this.ecNode = cs[1];
35707         this.iconNode = cs[2];
35708         var index = 3;
35709         if(cb){
35710             this.checkbox = cs[3];
35711             index++;
35712         }
35713         this.anchor = cs[index];
35714         this.textNode = cs[index].firstChild;
35715     },
35716
35717     getAnchor : function(){
35718         return this.anchor;
35719     },
35720
35721     getTextEl : function(){
35722         return this.textNode;
35723     },
35724
35725     getIconEl : function(){
35726         return this.iconNode;
35727     },
35728
35729     isChecked : function(){
35730         return this.checkbox ? this.checkbox.checked : false;
35731     },
35732
35733     updateExpandIcon : function(){
35734         if(this.rendered){
35735             var n = this.node, c1, c2;
35736             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35737             var hasChild = n.hasChildNodes();
35738             if(hasChild){
35739                 if(n.expanded){
35740                     cls += "-minus";
35741                     c1 = "x-tree-node-collapsed";
35742                     c2 = "x-tree-node-expanded";
35743                 }else{
35744                     cls += "-plus";
35745                     c1 = "x-tree-node-expanded";
35746                     c2 = "x-tree-node-collapsed";
35747                 }
35748                 if(this.wasLeaf){
35749                     this.removeClass("x-tree-node-leaf");
35750                     this.wasLeaf = false;
35751                 }
35752                 if(this.c1 != c1 || this.c2 != c2){
35753                     Roo.fly(this.elNode).replaceClass(c1, c2);
35754                     this.c1 = c1; this.c2 = c2;
35755                 }
35756             }else{
35757                 // this changes non-leafs into leafs if they have no children.
35758                 // it's not very rational behaviour..
35759                 
35760                 if(!this.wasLeaf && this.node.leaf){
35761                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35762                     delete this.c1;
35763                     delete this.c2;
35764                     this.wasLeaf = true;
35765                 }
35766             }
35767             var ecc = "x-tree-ec-icon "+cls;
35768             if(this.ecc != ecc){
35769                 this.ecNode.className = ecc;
35770                 this.ecc = ecc;
35771             }
35772         }
35773     },
35774
35775     getChildIndent : function(){
35776         if(!this.childIndent){
35777             var buf = [];
35778             var p = this.node;
35779             while(p){
35780                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35781                     if(!p.isLast()) {
35782                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35783                     } else {
35784                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35785                     }
35786                 }
35787                 p = p.parentNode;
35788             }
35789             this.childIndent = buf.join("");
35790         }
35791         return this.childIndent;
35792     },
35793
35794     renderIndent : function(){
35795         if(this.rendered){
35796             var indent = "";
35797             var p = this.node.parentNode;
35798             if(p){
35799                 indent = p.ui.getChildIndent();
35800             }
35801             if(this.indentMarkup != indent){ // don't rerender if not required
35802                 this.indentNode.innerHTML = indent;
35803                 this.indentMarkup = indent;
35804             }
35805             this.updateExpandIcon();
35806         }
35807     }
35808 };
35809
35810 Roo.tree.RootTreeNodeUI = function(){
35811     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35812 };
35813 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35814     render : function(){
35815         if(!this.rendered){
35816             var targetNode = this.node.ownerTree.innerCt.dom;
35817             this.node.expanded = true;
35818             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35819             this.wrap = this.ctNode = targetNode.firstChild;
35820         }
35821     },
35822     collapse : function(){
35823     },
35824     expand : function(){
35825     }
35826 });/*
35827  * Based on:
35828  * Ext JS Library 1.1.1
35829  * Copyright(c) 2006-2007, Ext JS, LLC.
35830  *
35831  * Originally Released Under LGPL - original licence link has changed is not relivant.
35832  *
35833  * Fork - LGPL
35834  * <script type="text/javascript">
35835  */
35836 /**
35837  * @class Roo.tree.TreeLoader
35838  * @extends Roo.util.Observable
35839  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35840  * nodes from a specified URL. The response must be a javascript Array definition
35841  * who's elements are node definition objects. eg:
35842  * <pre><code>
35843 {  success : true,
35844    data :      [
35845    
35846     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35847     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35848     ]
35849 }
35850
35851
35852 </code></pre>
35853  * <br><br>
35854  * The old style respose with just an array is still supported, but not recommended.
35855  * <br><br>
35856  *
35857  * A server request is sent, and child nodes are loaded only when a node is expanded.
35858  * The loading node's id is passed to the server under the parameter name "node" to
35859  * enable the server to produce the correct child nodes.
35860  * <br><br>
35861  * To pass extra parameters, an event handler may be attached to the "beforeload"
35862  * event, and the parameters specified in the TreeLoader's baseParams property:
35863  * <pre><code>
35864     myTreeLoader.on("beforeload", function(treeLoader, node) {
35865         this.baseParams.category = node.attributes.category;
35866     }, this);
35867 </code></pre><
35868  * This would pass an HTTP parameter called "category" to the server containing
35869  * the value of the Node's "category" attribute.
35870  * @constructor
35871  * Creates a new Treeloader.
35872  * @param {Object} config A config object containing config properties.
35873  */
35874 Roo.tree.TreeLoader = function(config){
35875     this.baseParams = {};
35876     this.requestMethod = "POST";
35877     Roo.apply(this, config);
35878
35879     this.addEvents({
35880     
35881         /**
35882          * @event beforeload
35883          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35884          * @param {Object} This TreeLoader object.
35885          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35886          * @param {Object} callback The callback function specified in the {@link #load} call.
35887          */
35888         beforeload : true,
35889         /**
35890          * @event load
35891          * Fires when the node has been successfuly loaded.
35892          * @param {Object} This TreeLoader object.
35893          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35894          * @param {Object} response The response object containing the data from the server.
35895          */
35896         load : true,
35897         /**
35898          * @event loadexception
35899          * Fires if the network request failed.
35900          * @param {Object} This TreeLoader object.
35901          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35902          * @param {Object} response The response object containing the data from the server.
35903          */
35904         loadexception : true,
35905         /**
35906          * @event create
35907          * Fires before a node is created, enabling you to return custom Node types 
35908          * @param {Object} This TreeLoader object.
35909          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35910          */
35911         create : true
35912     });
35913
35914     Roo.tree.TreeLoader.superclass.constructor.call(this);
35915 };
35916
35917 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35918     /**
35919     * @cfg {String} dataUrl The URL from which to request a Json string which
35920     * specifies an array of node definition object representing the child nodes
35921     * to be loaded.
35922     */
35923     /**
35924     * @cfg {String} requestMethod either GET or POST
35925     * defaults to POST (due to BC)
35926     * to be loaded.
35927     */
35928     /**
35929     * @cfg {Object} baseParams (optional) An object containing properties which
35930     * specify HTTP parameters to be passed to each request for child nodes.
35931     */
35932     /**
35933     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35934     * created by this loader. If the attributes sent by the server have an attribute in this object,
35935     * they take priority.
35936     */
35937     /**
35938     * @cfg {Object} uiProviders (optional) An object containing properties which
35939     * 
35940     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35941     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35942     * <i>uiProvider</i> attribute of a returned child node is a string rather
35943     * than a reference to a TreeNodeUI implementation, this that string value
35944     * is used as a property name in the uiProviders object. You can define the provider named
35945     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35946     */
35947     uiProviders : {},
35948
35949     /**
35950     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35951     * child nodes before loading.
35952     */
35953     clearOnLoad : true,
35954
35955     /**
35956     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35957     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35958     * Grid query { data : [ .....] }
35959     */
35960     
35961     root : false,
35962      /**
35963     * @cfg {String} queryParam (optional) 
35964     * Name of the query as it will be passed on the querystring (defaults to 'node')
35965     * eg. the request will be ?node=[id]
35966     */
35967     
35968     
35969     queryParam: false,
35970     
35971     /**
35972      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
35973      * This is called automatically when a node is expanded, but may be used to reload
35974      * a node (or append new children if the {@link #clearOnLoad} option is false.)
35975      * @param {Roo.tree.TreeNode} node
35976      * @param {Function} callback
35977      */
35978     load : function(node, callback){
35979         if(this.clearOnLoad){
35980             while(node.firstChild){
35981                 node.removeChild(node.firstChild);
35982             }
35983         }
35984         if(node.attributes.children){ // preloaded json children
35985             var cs = node.attributes.children;
35986             for(var i = 0, len = cs.length; i < len; i++){
35987                 node.appendChild(this.createNode(cs[i]));
35988             }
35989             if(typeof callback == "function"){
35990                 callback();
35991             }
35992         }else if(this.dataUrl){
35993             this.requestData(node, callback);
35994         }
35995     },
35996
35997     getParams: function(node){
35998         var buf = [], bp = this.baseParams;
35999         for(var key in bp){
36000             if(typeof bp[key] != "function"){
36001                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36002             }
36003         }
36004         var n = this.queryParam === false ? 'node' : this.queryParam;
36005         buf.push(n + "=", encodeURIComponent(node.id));
36006         return buf.join("");
36007     },
36008
36009     requestData : function(node, callback){
36010         if(this.fireEvent("beforeload", this, node, callback) !== false){
36011             this.transId = Roo.Ajax.request({
36012                 method:this.requestMethod,
36013                 url: this.dataUrl||this.url,
36014                 success: this.handleResponse,
36015                 failure: this.handleFailure,
36016                 scope: this,
36017                 argument: {callback: callback, node: node},
36018                 params: this.getParams(node)
36019             });
36020         }else{
36021             // if the load is cancelled, make sure we notify
36022             // the node that we are done
36023             if(typeof callback == "function"){
36024                 callback();
36025             }
36026         }
36027     },
36028
36029     isLoading : function(){
36030         return this.transId ? true : false;
36031     },
36032
36033     abort : function(){
36034         if(this.isLoading()){
36035             Roo.Ajax.abort(this.transId);
36036         }
36037     },
36038
36039     // private
36040     createNode : function(attr)
36041     {
36042         // apply baseAttrs, nice idea Corey!
36043         if(this.baseAttrs){
36044             Roo.applyIf(attr, this.baseAttrs);
36045         }
36046         if(this.applyLoader !== false){
36047             attr.loader = this;
36048         }
36049         // uiProvider = depreciated..
36050         
36051         if(typeof(attr.uiProvider) == 'string'){
36052            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36053                 /**  eval:var:attr */ eval(attr.uiProvider);
36054         }
36055         if(typeof(this.uiProviders['default']) != 'undefined') {
36056             attr.uiProvider = this.uiProviders['default'];
36057         }
36058         
36059         this.fireEvent('create', this, attr);
36060         
36061         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36062         return(attr.leaf ?
36063                         new Roo.tree.TreeNode(attr) :
36064                         new Roo.tree.AsyncTreeNode(attr));
36065     },
36066
36067     processResponse : function(response, node, callback)
36068     {
36069         var json = response.responseText;
36070         try {
36071             
36072             var o = Roo.decode(json);
36073             
36074             if (this.root === false && typeof(o.success) != undefined) {
36075                 this.root = 'data'; // the default behaviour for list like data..
36076                 }
36077                 
36078             if (this.root !== false &&  !o.success) {
36079                 // it's a failure condition.
36080                 var a = response.argument;
36081                 this.fireEvent("loadexception", this, a.node, response);
36082                 Roo.log("Load failed - should have a handler really");
36083                 return;
36084             }
36085             
36086             
36087             
36088             if (this.root !== false) {
36089                  o = o[this.root];
36090             }
36091             
36092             for(var i = 0, len = o.length; i < len; i++){
36093                 var n = this.createNode(o[i]);
36094                 if(n){
36095                     node.appendChild(n);
36096                 }
36097             }
36098             if(typeof callback == "function"){
36099                 callback(this, node);
36100             }
36101         }catch(e){
36102             this.handleFailure(response);
36103         }
36104     },
36105
36106     handleResponse : function(response){
36107         this.transId = false;
36108         var a = response.argument;
36109         this.processResponse(response, a.node, a.callback);
36110         this.fireEvent("load", this, a.node, response);
36111     },
36112
36113     handleFailure : function(response)
36114     {
36115         // should handle failure better..
36116         this.transId = false;
36117         var a = response.argument;
36118         this.fireEvent("loadexception", this, a.node, response);
36119         if(typeof a.callback == "function"){
36120             a.callback(this, a.node);
36121         }
36122     }
36123 });/*
36124  * Based on:
36125  * Ext JS Library 1.1.1
36126  * Copyright(c) 2006-2007, Ext JS, LLC.
36127  *
36128  * Originally Released Under LGPL - original licence link has changed is not relivant.
36129  *
36130  * Fork - LGPL
36131  * <script type="text/javascript">
36132  */
36133
36134 /**
36135 * @class Roo.tree.TreeFilter
36136 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36137 * @param {TreePanel} tree
36138 * @param {Object} config (optional)
36139  */
36140 Roo.tree.TreeFilter = function(tree, config){
36141     this.tree = tree;
36142     this.filtered = {};
36143     Roo.apply(this, config);
36144 };
36145
36146 Roo.tree.TreeFilter.prototype = {
36147     clearBlank:false,
36148     reverse:false,
36149     autoClear:false,
36150     remove:false,
36151
36152      /**
36153      * Filter the data by a specific attribute.
36154      * @param {String/RegExp} value Either string that the attribute value
36155      * should start with or a RegExp to test against the attribute
36156      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36157      * @param {TreeNode} startNode (optional) The node to start the filter at.
36158      */
36159     filter : function(value, attr, startNode){
36160         attr = attr || "text";
36161         var f;
36162         if(typeof value == "string"){
36163             var vlen = value.length;
36164             // auto clear empty filter
36165             if(vlen == 0 && this.clearBlank){
36166                 this.clear();
36167                 return;
36168             }
36169             value = value.toLowerCase();
36170             f = function(n){
36171                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36172             };
36173         }else if(value.exec){ // regex?
36174             f = function(n){
36175                 return value.test(n.attributes[attr]);
36176             };
36177         }else{
36178             throw 'Illegal filter type, must be string or regex';
36179         }
36180         this.filterBy(f, null, startNode);
36181         },
36182
36183     /**
36184      * Filter by a function. The passed function will be called with each
36185      * node in the tree (or from the startNode). If the function returns true, the node is kept
36186      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36187      * @param {Function} fn The filter function
36188      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36189      */
36190     filterBy : function(fn, scope, startNode){
36191         startNode = startNode || this.tree.root;
36192         if(this.autoClear){
36193             this.clear();
36194         }
36195         var af = this.filtered, rv = this.reverse;
36196         var f = function(n){
36197             if(n == startNode){
36198                 return true;
36199             }
36200             if(af[n.id]){
36201                 return false;
36202             }
36203             var m = fn.call(scope || n, n);
36204             if(!m || rv){
36205                 af[n.id] = n;
36206                 n.ui.hide();
36207                 return false;
36208             }
36209             return true;
36210         };
36211         startNode.cascade(f);
36212         if(this.remove){
36213            for(var id in af){
36214                if(typeof id != "function"){
36215                    var n = af[id];
36216                    if(n && n.parentNode){
36217                        n.parentNode.removeChild(n);
36218                    }
36219                }
36220            }
36221         }
36222     },
36223
36224     /**
36225      * Clears the current filter. Note: with the "remove" option
36226      * set a filter cannot be cleared.
36227      */
36228     clear : function(){
36229         var t = this.tree;
36230         var af = this.filtered;
36231         for(var id in af){
36232             if(typeof id != "function"){
36233                 var n = af[id];
36234                 if(n){
36235                     n.ui.show();
36236                 }
36237             }
36238         }
36239         this.filtered = {};
36240     }
36241 };
36242 /*
36243  * Based on:
36244  * Ext JS Library 1.1.1
36245  * Copyright(c) 2006-2007, Ext JS, LLC.
36246  *
36247  * Originally Released Under LGPL - original licence link has changed is not relivant.
36248  *
36249  * Fork - LGPL
36250  * <script type="text/javascript">
36251  */
36252  
36253
36254 /**
36255  * @class Roo.tree.TreeSorter
36256  * Provides sorting of nodes in a TreePanel
36257  * 
36258  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36259  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36260  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36261  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36262  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36263  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36264  * @constructor
36265  * @param {TreePanel} tree
36266  * @param {Object} config
36267  */
36268 Roo.tree.TreeSorter = function(tree, config){
36269     Roo.apply(this, config);
36270     tree.on("beforechildrenrendered", this.doSort, this);
36271     tree.on("append", this.updateSort, this);
36272     tree.on("insert", this.updateSort, this);
36273     
36274     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36275     var p = this.property || "text";
36276     var sortType = this.sortType;
36277     var fs = this.folderSort;
36278     var cs = this.caseSensitive === true;
36279     var leafAttr = this.leafAttr || 'leaf';
36280
36281     this.sortFn = function(n1, n2){
36282         if(fs){
36283             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36284                 return 1;
36285             }
36286             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36287                 return -1;
36288             }
36289         }
36290         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36291         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36292         if(v1 < v2){
36293                         return dsc ? +1 : -1;
36294                 }else if(v1 > v2){
36295                         return dsc ? -1 : +1;
36296         }else{
36297                 return 0;
36298         }
36299     };
36300 };
36301
36302 Roo.tree.TreeSorter.prototype = {
36303     doSort : function(node){
36304         node.sort(this.sortFn);
36305     },
36306     
36307     compareNodes : function(n1, n2){
36308         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36309     },
36310     
36311     updateSort : function(tree, node){
36312         if(node.childrenRendered){
36313             this.doSort.defer(1, this, [node]);
36314         }
36315     }
36316 };/*
36317  * Based on:
36318  * Ext JS Library 1.1.1
36319  * Copyright(c) 2006-2007, Ext JS, LLC.
36320  *
36321  * Originally Released Under LGPL - original licence link has changed is not relivant.
36322  *
36323  * Fork - LGPL
36324  * <script type="text/javascript">
36325  */
36326
36327 if(Roo.dd.DropZone){
36328     
36329 Roo.tree.TreeDropZone = function(tree, config){
36330     this.allowParentInsert = false;
36331     this.allowContainerDrop = false;
36332     this.appendOnly = false;
36333     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36334     this.tree = tree;
36335     this.lastInsertClass = "x-tree-no-status";
36336     this.dragOverData = {};
36337 };
36338
36339 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36340     ddGroup : "TreeDD",
36341     scroll:  true,
36342     
36343     expandDelay : 1000,
36344     
36345     expandNode : function(node){
36346         if(node.hasChildNodes() && !node.isExpanded()){
36347             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36348         }
36349     },
36350     
36351     queueExpand : function(node){
36352         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36353     },
36354     
36355     cancelExpand : function(){
36356         if(this.expandProcId){
36357             clearTimeout(this.expandProcId);
36358             this.expandProcId = false;
36359         }
36360     },
36361     
36362     isValidDropPoint : function(n, pt, dd, e, data){
36363         if(!n || !data){ return false; }
36364         var targetNode = n.node;
36365         var dropNode = data.node;
36366         // default drop rules
36367         if(!(targetNode && targetNode.isTarget && pt)){
36368             return false;
36369         }
36370         if(pt == "append" && targetNode.allowChildren === false){
36371             return false;
36372         }
36373         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36374             return false;
36375         }
36376         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36377             return false;
36378         }
36379         // reuse the object
36380         var overEvent = this.dragOverData;
36381         overEvent.tree = this.tree;
36382         overEvent.target = targetNode;
36383         overEvent.data = data;
36384         overEvent.point = pt;
36385         overEvent.source = dd;
36386         overEvent.rawEvent = e;
36387         overEvent.dropNode = dropNode;
36388         overEvent.cancel = false;  
36389         var result = this.tree.fireEvent("nodedragover", overEvent);
36390         return overEvent.cancel === false && result !== false;
36391     },
36392     
36393     getDropPoint : function(e, n, dd)
36394     {
36395         var tn = n.node;
36396         if(tn.isRoot){
36397             return tn.allowChildren !== false ? "append" : false; // always append for root
36398         }
36399         var dragEl = n.ddel;
36400         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36401         var y = Roo.lib.Event.getPageY(e);
36402         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36403         
36404         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36405         var noAppend = tn.allowChildren === false;
36406         if(this.appendOnly || tn.parentNode.allowChildren === false){
36407             return noAppend ? false : "append";
36408         }
36409         var noBelow = false;
36410         if(!this.allowParentInsert){
36411             noBelow = tn.hasChildNodes() && tn.isExpanded();
36412         }
36413         var q = (b - t) / (noAppend ? 2 : 3);
36414         if(y >= t && y < (t + q)){
36415             return "above";
36416         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36417             return "below";
36418         }else{
36419             return "append";
36420         }
36421     },
36422     
36423     onNodeEnter : function(n, dd, e, data)
36424     {
36425         this.cancelExpand();
36426     },
36427     
36428     onNodeOver : function(n, dd, e, data)
36429     {
36430        
36431         var pt = this.getDropPoint(e, n, dd);
36432         var node = n.node;
36433         
36434         // auto node expand check
36435         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36436             this.queueExpand(node);
36437         }else if(pt != "append"){
36438             this.cancelExpand();
36439         }
36440         
36441         // set the insert point style on the target node
36442         var returnCls = this.dropNotAllowed;
36443         if(this.isValidDropPoint(n, pt, dd, e, data)){
36444            if(pt){
36445                var el = n.ddel;
36446                var cls;
36447                if(pt == "above"){
36448                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36449                    cls = "x-tree-drag-insert-above";
36450                }else if(pt == "below"){
36451                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36452                    cls = "x-tree-drag-insert-below";
36453                }else{
36454                    returnCls = "x-tree-drop-ok-append";
36455                    cls = "x-tree-drag-append";
36456                }
36457                if(this.lastInsertClass != cls){
36458                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36459                    this.lastInsertClass = cls;
36460                }
36461            }
36462        }
36463        return returnCls;
36464     },
36465     
36466     onNodeOut : function(n, dd, e, data){
36467         
36468         this.cancelExpand();
36469         this.removeDropIndicators(n);
36470     },
36471     
36472     onNodeDrop : function(n, dd, e, data){
36473         var point = this.getDropPoint(e, n, dd);
36474         var targetNode = n.node;
36475         targetNode.ui.startDrop();
36476         if(!this.isValidDropPoint(n, point, dd, e, data)){
36477             targetNode.ui.endDrop();
36478             return false;
36479         }
36480         // first try to find the drop node
36481         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36482         var dropEvent = {
36483             tree : this.tree,
36484             target: targetNode,
36485             data: data,
36486             point: point,
36487             source: dd,
36488             rawEvent: e,
36489             dropNode: dropNode,
36490             cancel: !dropNode   
36491         };
36492         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36493         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36494             targetNode.ui.endDrop();
36495             return false;
36496         }
36497         // allow target changing
36498         targetNode = dropEvent.target;
36499         if(point == "append" && !targetNode.isExpanded()){
36500             targetNode.expand(false, null, function(){
36501                 this.completeDrop(dropEvent);
36502             }.createDelegate(this));
36503         }else{
36504             this.completeDrop(dropEvent);
36505         }
36506         return true;
36507     },
36508     
36509     completeDrop : function(de){
36510         var ns = de.dropNode, p = de.point, t = de.target;
36511         if(!(ns instanceof Array)){
36512             ns = [ns];
36513         }
36514         var n;
36515         for(var i = 0, len = ns.length; i < len; i++){
36516             n = ns[i];
36517             if(p == "above"){
36518                 t.parentNode.insertBefore(n, t);
36519             }else if(p == "below"){
36520                 t.parentNode.insertBefore(n, t.nextSibling);
36521             }else{
36522                 t.appendChild(n);
36523             }
36524         }
36525         n.ui.focus();
36526         if(this.tree.hlDrop){
36527             n.ui.highlight();
36528         }
36529         t.ui.endDrop();
36530         this.tree.fireEvent("nodedrop", de);
36531     },
36532     
36533     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36534         if(this.tree.hlDrop){
36535             dropNode.ui.focus();
36536             dropNode.ui.highlight();
36537         }
36538         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36539     },
36540     
36541     getTree : function(){
36542         return this.tree;
36543     },
36544     
36545     removeDropIndicators : function(n){
36546         if(n && n.ddel){
36547             var el = n.ddel;
36548             Roo.fly(el).removeClass([
36549                     "x-tree-drag-insert-above",
36550                     "x-tree-drag-insert-below",
36551                     "x-tree-drag-append"]);
36552             this.lastInsertClass = "_noclass";
36553         }
36554     },
36555     
36556     beforeDragDrop : function(target, e, id){
36557         this.cancelExpand();
36558         return true;
36559     },
36560     
36561     afterRepair : function(data){
36562         if(data && Roo.enableFx){
36563             data.node.ui.highlight();
36564         }
36565         this.hideProxy();
36566     } 
36567     
36568 });
36569
36570 }
36571 /*
36572  * Based on:
36573  * Ext JS Library 1.1.1
36574  * Copyright(c) 2006-2007, Ext JS, LLC.
36575  *
36576  * Originally Released Under LGPL - original licence link has changed is not relivant.
36577  *
36578  * Fork - LGPL
36579  * <script type="text/javascript">
36580  */
36581  
36582
36583 if(Roo.dd.DragZone){
36584 Roo.tree.TreeDragZone = function(tree, config){
36585     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36586     this.tree = tree;
36587 };
36588
36589 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36590     ddGroup : "TreeDD",
36591    
36592     onBeforeDrag : function(data, e){
36593         var n = data.node;
36594         return n && n.draggable && !n.disabled;
36595     },
36596      
36597     
36598     onInitDrag : function(e){
36599         var data = this.dragData;
36600         this.tree.getSelectionModel().select(data.node);
36601         this.proxy.update("");
36602         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36603         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36604     },
36605     
36606     getRepairXY : function(e, data){
36607         return data.node.ui.getDDRepairXY();
36608     },
36609     
36610     onEndDrag : function(data, e){
36611         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36612         
36613         
36614     },
36615     
36616     onValidDrop : function(dd, e, id){
36617         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36618         this.hideProxy();
36619     },
36620     
36621     beforeInvalidDrop : function(e, id){
36622         // this scrolls the original position back into view
36623         var sm = this.tree.getSelectionModel();
36624         sm.clearSelections();
36625         sm.select(this.dragData.node);
36626     }
36627 });
36628 }/*
36629  * Based on:
36630  * Ext JS Library 1.1.1
36631  * Copyright(c) 2006-2007, Ext JS, LLC.
36632  *
36633  * Originally Released Under LGPL - original licence link has changed is not relivant.
36634  *
36635  * Fork - LGPL
36636  * <script type="text/javascript">
36637  */
36638 /**
36639  * @class Roo.tree.TreeEditor
36640  * @extends Roo.Editor
36641  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36642  * as the editor field.
36643  * @constructor
36644  * @param {Object} config (used to be the tree panel.)
36645  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36646  * 
36647  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36648  * @cfg {Roo.form.TextField|Object} field The field configuration
36649  *
36650  * 
36651  */
36652 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36653     var tree = config;
36654     var field;
36655     if (oldconfig) { // old style..
36656         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36657     } else {
36658         // new style..
36659         tree = config.tree;
36660         config.field = config.field  || {};
36661         config.field.xtype = 'TextField';
36662         field = Roo.factory(config.field, Roo.form);
36663     }
36664     config = config || {};
36665     
36666     
36667     this.addEvents({
36668         /**
36669          * @event beforenodeedit
36670          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36671          * false from the handler of this event.
36672          * @param {Editor} this
36673          * @param {Roo.tree.Node} node 
36674          */
36675         "beforenodeedit" : true
36676     });
36677     
36678     //Roo.log(config);
36679     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36680
36681     this.tree = tree;
36682
36683     tree.on('beforeclick', this.beforeNodeClick, this);
36684     tree.getTreeEl().on('mousedown', this.hide, this);
36685     this.on('complete', this.updateNode, this);
36686     this.on('beforestartedit', this.fitToTree, this);
36687     this.on('startedit', this.bindScroll, this, {delay:10});
36688     this.on('specialkey', this.onSpecialKey, this);
36689 };
36690
36691 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36692     /**
36693      * @cfg {String} alignment
36694      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36695      */
36696     alignment: "l-l",
36697     // inherit
36698     autoSize: false,
36699     /**
36700      * @cfg {Boolean} hideEl
36701      * True to hide the bound element while the editor is displayed (defaults to false)
36702      */
36703     hideEl : false,
36704     /**
36705      * @cfg {String} cls
36706      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36707      */
36708     cls: "x-small-editor x-tree-editor",
36709     /**
36710      * @cfg {Boolean} shim
36711      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36712      */
36713     shim:false,
36714     // inherit
36715     shadow:"frame",
36716     /**
36717      * @cfg {Number} maxWidth
36718      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36719      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36720      * scroll and client offsets into account prior to each edit.
36721      */
36722     maxWidth: 250,
36723
36724     editDelay : 350,
36725
36726     // private
36727     fitToTree : function(ed, el){
36728         var td = this.tree.getTreeEl().dom, nd = el.dom;
36729         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36730             td.scrollLeft = nd.offsetLeft;
36731         }
36732         var w = Math.min(
36733                 this.maxWidth,
36734                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36735         this.setSize(w, '');
36736         
36737         return this.fireEvent('beforenodeedit', this, this.editNode);
36738         
36739     },
36740
36741     // private
36742     triggerEdit : function(node){
36743         this.completeEdit();
36744         this.editNode = node;
36745         this.startEdit(node.ui.textNode, node.text);
36746     },
36747
36748     // private
36749     bindScroll : function(){
36750         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36751     },
36752
36753     // private
36754     beforeNodeClick : function(node, e){
36755         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36756         this.lastClick = new Date();
36757         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36758             e.stopEvent();
36759             this.triggerEdit(node);
36760             return false;
36761         }
36762         return true;
36763     },
36764
36765     // private
36766     updateNode : function(ed, value){
36767         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36768         this.editNode.setText(value);
36769     },
36770
36771     // private
36772     onHide : function(){
36773         Roo.tree.TreeEditor.superclass.onHide.call(this);
36774         if(this.editNode){
36775             this.editNode.ui.focus();
36776         }
36777     },
36778
36779     // private
36780     onSpecialKey : function(field, e){
36781         var k = e.getKey();
36782         if(k == e.ESC){
36783             e.stopEvent();
36784             this.cancelEdit();
36785         }else if(k == e.ENTER && !e.hasModifier()){
36786             e.stopEvent();
36787             this.completeEdit();
36788         }
36789     }
36790 });//<Script type="text/javascript">
36791 /*
36792  * Based on:
36793  * Ext JS Library 1.1.1
36794  * Copyright(c) 2006-2007, Ext JS, LLC.
36795  *
36796  * Originally Released Under LGPL - original licence link has changed is not relivant.
36797  *
36798  * Fork - LGPL
36799  * <script type="text/javascript">
36800  */
36801  
36802 /**
36803  * Not documented??? - probably should be...
36804  */
36805
36806 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36807     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36808     
36809     renderElements : function(n, a, targetNode, bulkRender){
36810         //consel.log("renderElements?");
36811         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36812
36813         var t = n.getOwnerTree();
36814         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36815         
36816         var cols = t.columns;
36817         var bw = t.borderWidth;
36818         var c = cols[0];
36819         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36820          var cb = typeof a.checked == "boolean";
36821         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36822         var colcls = 'x-t-' + tid + '-c0';
36823         var buf = [
36824             '<li class="x-tree-node">',
36825             
36826                 
36827                 '<div class="x-tree-node-el ', a.cls,'">',
36828                     // extran...
36829                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36830                 
36831                 
36832                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36833                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36834                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36835                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36836                            (a.iconCls ? ' '+a.iconCls : ''),
36837                            '" unselectable="on" />',
36838                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36839                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36840                              
36841                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36842                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36843                             '<span unselectable="on" qtip="' + tx + '">',
36844                              tx,
36845                              '</span></a>' ,
36846                     '</div>',
36847                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36848                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36849                  ];
36850         for(var i = 1, len = cols.length; i < len; i++){
36851             c = cols[i];
36852             colcls = 'x-t-' + tid + '-c' +i;
36853             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36854             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36855                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36856                       "</div>");
36857          }
36858          
36859          buf.push(
36860             '</a>',
36861             '<div class="x-clear"></div></div>',
36862             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36863             "</li>");
36864         
36865         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36866             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36867                                 n.nextSibling.ui.getEl(), buf.join(""));
36868         }else{
36869             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36870         }
36871         var el = this.wrap.firstChild;
36872         this.elRow = el;
36873         this.elNode = el.firstChild;
36874         this.ranchor = el.childNodes[1];
36875         this.ctNode = this.wrap.childNodes[1];
36876         var cs = el.firstChild.childNodes;
36877         this.indentNode = cs[0];
36878         this.ecNode = cs[1];
36879         this.iconNode = cs[2];
36880         var index = 3;
36881         if(cb){
36882             this.checkbox = cs[3];
36883             index++;
36884         }
36885         this.anchor = cs[index];
36886         
36887         this.textNode = cs[index].firstChild;
36888         
36889         //el.on("click", this.onClick, this);
36890         //el.on("dblclick", this.onDblClick, this);
36891         
36892         
36893        // console.log(this);
36894     },
36895     initEvents : function(){
36896         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36897         
36898             
36899         var a = this.ranchor;
36900
36901         var el = Roo.get(a);
36902
36903         if(Roo.isOpera){ // opera render bug ignores the CSS
36904             el.setStyle("text-decoration", "none");
36905         }
36906
36907         el.on("click", this.onClick, this);
36908         el.on("dblclick", this.onDblClick, this);
36909         el.on("contextmenu", this.onContextMenu, this);
36910         
36911     },
36912     
36913     /*onSelectedChange : function(state){
36914         if(state){
36915             this.focus();
36916             this.addClass("x-tree-selected");
36917         }else{
36918             //this.blur();
36919             this.removeClass("x-tree-selected");
36920         }
36921     },*/
36922     addClass : function(cls){
36923         if(this.elRow){
36924             Roo.fly(this.elRow).addClass(cls);
36925         }
36926         
36927     },
36928     
36929     
36930     removeClass : function(cls){
36931         if(this.elRow){
36932             Roo.fly(this.elRow).removeClass(cls);
36933         }
36934     }
36935
36936     
36937     
36938 });//<Script type="text/javascript">
36939
36940 /*
36941  * Based on:
36942  * Ext JS Library 1.1.1
36943  * Copyright(c) 2006-2007, Ext JS, LLC.
36944  *
36945  * Originally Released Under LGPL - original licence link has changed is not relivant.
36946  *
36947  * Fork - LGPL
36948  * <script type="text/javascript">
36949  */
36950  
36951
36952 /**
36953  * @class Roo.tree.ColumnTree
36954  * @extends Roo.data.TreePanel
36955  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36956  * @cfg {int} borderWidth  compined right/left border allowance
36957  * @constructor
36958  * @param {String/HTMLElement/Element} el The container element
36959  * @param {Object} config
36960  */
36961 Roo.tree.ColumnTree =  function(el, config)
36962 {
36963    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36964    this.addEvents({
36965         /**
36966         * @event resize
36967         * Fire this event on a container when it resizes
36968         * @param {int} w Width
36969         * @param {int} h Height
36970         */
36971        "resize" : true
36972     });
36973     this.on('resize', this.onResize, this);
36974 };
36975
36976 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
36977     //lines:false,
36978     
36979     
36980     borderWidth: Roo.isBorderBox ? 0 : 2, 
36981     headEls : false,
36982     
36983     render : function(){
36984         // add the header.....
36985        
36986         Roo.tree.ColumnTree.superclass.render.apply(this);
36987         
36988         this.el.addClass('x-column-tree');
36989         
36990         this.headers = this.el.createChild(
36991             {cls:'x-tree-headers'},this.innerCt.dom);
36992    
36993         var cols = this.columns, c;
36994         var totalWidth = 0;
36995         this.headEls = [];
36996         var  len = cols.length;
36997         for(var i = 0; i < len; i++){
36998              c = cols[i];
36999              totalWidth += c.width;
37000             this.headEls.push(this.headers.createChild({
37001                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37002                  cn: {
37003                      cls:'x-tree-hd-text',
37004                      html: c.header
37005                  },
37006                  style:'width:'+(c.width-this.borderWidth)+'px;'
37007              }));
37008         }
37009         this.headers.createChild({cls:'x-clear'});
37010         // prevent floats from wrapping when clipped
37011         this.headers.setWidth(totalWidth);
37012         //this.innerCt.setWidth(totalWidth);
37013         this.innerCt.setStyle({ overflow: 'auto' });
37014         this.onResize(this.width, this.height);
37015              
37016         
37017     },
37018     onResize : function(w,h)
37019     {
37020         this.height = h;
37021         this.width = w;
37022         // resize cols..
37023         this.innerCt.setWidth(this.width);
37024         this.innerCt.setHeight(this.height-20);
37025         
37026         // headers...
37027         var cols = this.columns, c;
37028         var totalWidth = 0;
37029         var expEl = false;
37030         var len = cols.length;
37031         for(var i = 0; i < len; i++){
37032             c = cols[i];
37033             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37034                 // it's the expander..
37035                 expEl  = this.headEls[i];
37036                 continue;
37037             }
37038             totalWidth += c.width;
37039             
37040         }
37041         if (expEl) {
37042             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37043         }
37044         this.headers.setWidth(w-20);
37045
37046         
37047         
37048         
37049     }
37050 });
37051 /*
37052  * Based on:
37053  * Ext JS Library 1.1.1
37054  * Copyright(c) 2006-2007, Ext JS, LLC.
37055  *
37056  * Originally Released Under LGPL - original licence link has changed is not relivant.
37057  *
37058  * Fork - LGPL
37059  * <script type="text/javascript">
37060  */
37061  
37062 /**
37063  * @class Roo.menu.Menu
37064  * @extends Roo.util.Observable
37065  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37066  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37067  * @constructor
37068  * Creates a new Menu
37069  * @param {Object} config Configuration options
37070  */
37071 Roo.menu.Menu = function(config){
37072     Roo.apply(this, config);
37073     this.id = this.id || Roo.id();
37074     this.addEvents({
37075         /**
37076          * @event beforeshow
37077          * Fires before this menu is displayed
37078          * @param {Roo.menu.Menu} this
37079          */
37080         beforeshow : true,
37081         /**
37082          * @event beforehide
37083          * Fires before this menu is hidden
37084          * @param {Roo.menu.Menu} this
37085          */
37086         beforehide : true,
37087         /**
37088          * @event show
37089          * Fires after this menu is displayed
37090          * @param {Roo.menu.Menu} this
37091          */
37092         show : true,
37093         /**
37094          * @event hide
37095          * Fires after this menu is hidden
37096          * @param {Roo.menu.Menu} this
37097          */
37098         hide : true,
37099         /**
37100          * @event click
37101          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37102          * @param {Roo.menu.Menu} this
37103          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37104          * @param {Roo.EventObject} e
37105          */
37106         click : true,
37107         /**
37108          * @event mouseover
37109          * Fires when the mouse is hovering over this menu
37110          * @param {Roo.menu.Menu} this
37111          * @param {Roo.EventObject} e
37112          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37113          */
37114         mouseover : true,
37115         /**
37116          * @event mouseout
37117          * Fires when the mouse exits this menu
37118          * @param {Roo.menu.Menu} this
37119          * @param {Roo.EventObject} e
37120          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37121          */
37122         mouseout : true,
37123         /**
37124          * @event itemclick
37125          * Fires when a menu item contained in this menu is clicked
37126          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37127          * @param {Roo.EventObject} e
37128          */
37129         itemclick: true
37130     });
37131     if (this.registerMenu) {
37132         Roo.menu.MenuMgr.register(this);
37133     }
37134     
37135     var mis = this.items;
37136     this.items = new Roo.util.MixedCollection();
37137     if(mis){
37138         this.add.apply(this, mis);
37139     }
37140 };
37141
37142 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37143     /**
37144      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37145      */
37146     minWidth : 120,
37147     /**
37148      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37149      * for bottom-right shadow (defaults to "sides")
37150      */
37151     shadow : "sides",
37152     /**
37153      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37154      * this menu (defaults to "tl-tr?")
37155      */
37156     subMenuAlign : "tl-tr?",
37157     /**
37158      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37159      * relative to its element of origin (defaults to "tl-bl?")
37160      */
37161     defaultAlign : "tl-bl?",
37162     /**
37163      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37164      */
37165     allowOtherMenus : false,
37166     /**
37167      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37168      */
37169     registerMenu : true,
37170
37171     hidden:true,
37172
37173     // private
37174     render : function(){
37175         if(this.el){
37176             return;
37177         }
37178         var el = this.el = new Roo.Layer({
37179             cls: "x-menu",
37180             shadow:this.shadow,
37181             constrain: false,
37182             parentEl: this.parentEl || document.body,
37183             zindex:15000
37184         });
37185
37186         this.keyNav = new Roo.menu.MenuNav(this);
37187
37188         if(this.plain){
37189             el.addClass("x-menu-plain");
37190         }
37191         if(this.cls){
37192             el.addClass(this.cls);
37193         }
37194         // generic focus element
37195         this.focusEl = el.createChild({
37196             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37197         });
37198         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37199         //disabling touch- as it's causing issues ..
37200         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37201         ul.on('click'   , this.onClick, this);
37202         
37203         
37204         ul.on("mouseover", this.onMouseOver, this);
37205         ul.on("mouseout", this.onMouseOut, this);
37206         this.items.each(function(item){
37207             if (item.hidden) {
37208                 return;
37209             }
37210             
37211             var li = document.createElement("li");
37212             li.className = "x-menu-list-item";
37213             ul.dom.appendChild(li);
37214             item.render(li, this);
37215         }, this);
37216         this.ul = ul;
37217         this.autoWidth();
37218     },
37219
37220     // private
37221     autoWidth : function(){
37222         var el = this.el, ul = this.ul;
37223         if(!el){
37224             return;
37225         }
37226         var w = this.width;
37227         if(w){
37228             el.setWidth(w);
37229         }else if(Roo.isIE){
37230             el.setWidth(this.minWidth);
37231             var t = el.dom.offsetWidth; // force recalc
37232             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37233         }
37234     },
37235
37236     // private
37237     delayAutoWidth : function(){
37238         if(this.rendered){
37239             if(!this.awTask){
37240                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37241             }
37242             this.awTask.delay(20);
37243         }
37244     },
37245
37246     // private
37247     findTargetItem : function(e){
37248         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37249         if(t && t.menuItemId){
37250             return this.items.get(t.menuItemId);
37251         }
37252     },
37253
37254     // private
37255     onClick : function(e){
37256         Roo.log("menu.onClick");
37257         var t = this.findTargetItem(e);
37258         if(!t){
37259             return;
37260         }
37261         Roo.log(e);
37262         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37263             if(t == this.activeItem && t.shouldDeactivate(e)){
37264                 this.activeItem.deactivate();
37265                 delete this.activeItem;
37266                 return;
37267             }
37268             if(t.canActivate){
37269                 this.setActiveItem(t, true);
37270             }
37271             return;
37272             
37273             
37274         }
37275         
37276         t.onClick(e);
37277         this.fireEvent("click", this, t, e);
37278     },
37279
37280     // private
37281     setActiveItem : function(item, autoExpand){
37282         if(item != this.activeItem){
37283             if(this.activeItem){
37284                 this.activeItem.deactivate();
37285             }
37286             this.activeItem = item;
37287             item.activate(autoExpand);
37288         }else if(autoExpand){
37289             item.expandMenu();
37290         }
37291     },
37292
37293     // private
37294     tryActivate : function(start, step){
37295         var items = this.items;
37296         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37297             var item = items.get(i);
37298             if(!item.disabled && item.canActivate){
37299                 this.setActiveItem(item, false);
37300                 return item;
37301             }
37302         }
37303         return false;
37304     },
37305
37306     // private
37307     onMouseOver : function(e){
37308         var t;
37309         if(t = this.findTargetItem(e)){
37310             if(t.canActivate && !t.disabled){
37311                 this.setActiveItem(t, true);
37312             }
37313         }
37314         this.fireEvent("mouseover", this, e, t);
37315     },
37316
37317     // private
37318     onMouseOut : function(e){
37319         var t;
37320         if(t = this.findTargetItem(e)){
37321             if(t == this.activeItem && t.shouldDeactivate(e)){
37322                 this.activeItem.deactivate();
37323                 delete this.activeItem;
37324             }
37325         }
37326         this.fireEvent("mouseout", this, e, t);
37327     },
37328
37329     /**
37330      * Read-only.  Returns true if the menu is currently displayed, else false.
37331      * @type Boolean
37332      */
37333     isVisible : function(){
37334         return this.el && !this.hidden;
37335     },
37336
37337     /**
37338      * Displays this menu relative to another element
37339      * @param {String/HTMLElement/Roo.Element} element The element to align to
37340      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37341      * the element (defaults to this.defaultAlign)
37342      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37343      */
37344     show : function(el, pos, parentMenu){
37345         this.parentMenu = parentMenu;
37346         if(!this.el){
37347             this.render();
37348         }
37349         this.fireEvent("beforeshow", this);
37350         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37351     },
37352
37353     /**
37354      * Displays this menu at a specific xy position
37355      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37356      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37357      */
37358     showAt : function(xy, parentMenu, /* private: */_e){
37359         this.parentMenu = parentMenu;
37360         if(!this.el){
37361             this.render();
37362         }
37363         if(_e !== false){
37364             this.fireEvent("beforeshow", this);
37365             xy = this.el.adjustForConstraints(xy);
37366         }
37367         this.el.setXY(xy);
37368         this.el.show();
37369         this.hidden = false;
37370         this.focus();
37371         this.fireEvent("show", this);
37372     },
37373
37374     focus : function(){
37375         if(!this.hidden){
37376             this.doFocus.defer(50, this);
37377         }
37378     },
37379
37380     doFocus : function(){
37381         if(!this.hidden){
37382             this.focusEl.focus();
37383         }
37384     },
37385
37386     /**
37387      * Hides this menu and optionally all parent menus
37388      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37389      */
37390     hide : function(deep){
37391         if(this.el && this.isVisible()){
37392             this.fireEvent("beforehide", this);
37393             if(this.activeItem){
37394                 this.activeItem.deactivate();
37395                 this.activeItem = null;
37396             }
37397             this.el.hide();
37398             this.hidden = true;
37399             this.fireEvent("hide", this);
37400         }
37401         if(deep === true && this.parentMenu){
37402             this.parentMenu.hide(true);
37403         }
37404     },
37405
37406     /**
37407      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37408      * Any of the following are valid:
37409      * <ul>
37410      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37411      * <li>An HTMLElement object which will be converted to a menu item</li>
37412      * <li>A menu item config object that will be created as a new menu item</li>
37413      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37414      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37415      * </ul>
37416      * Usage:
37417      * <pre><code>
37418 // Create the menu
37419 var menu = new Roo.menu.Menu();
37420
37421 // Create a menu item to add by reference
37422 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37423
37424 // Add a bunch of items at once using different methods.
37425 // Only the last item added will be returned.
37426 var item = menu.add(
37427     menuItem,                // add existing item by ref
37428     'Dynamic Item',          // new TextItem
37429     '-',                     // new separator
37430     { text: 'Config Item' }  // new item by config
37431 );
37432 </code></pre>
37433      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37434      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37435      */
37436     add : function(){
37437         var a = arguments, l = a.length, item;
37438         for(var i = 0; i < l; i++){
37439             var el = a[i];
37440             if ((typeof(el) == "object") && el.xtype && el.xns) {
37441                 el = Roo.factory(el, Roo.menu);
37442             }
37443             
37444             if(el.render){ // some kind of Item
37445                 item = this.addItem(el);
37446             }else if(typeof el == "string"){ // string
37447                 if(el == "separator" || el == "-"){
37448                     item = this.addSeparator();
37449                 }else{
37450                     item = this.addText(el);
37451                 }
37452             }else if(el.tagName || el.el){ // element
37453                 item = this.addElement(el);
37454             }else if(typeof el == "object"){ // must be menu item config?
37455                 item = this.addMenuItem(el);
37456             }
37457         }
37458         return item;
37459     },
37460
37461     /**
37462      * Returns this menu's underlying {@link Roo.Element} object
37463      * @return {Roo.Element} The element
37464      */
37465     getEl : function(){
37466         if(!this.el){
37467             this.render();
37468         }
37469         return this.el;
37470     },
37471
37472     /**
37473      * Adds a separator bar to the menu
37474      * @return {Roo.menu.Item} The menu item that was added
37475      */
37476     addSeparator : function(){
37477         return this.addItem(new Roo.menu.Separator());
37478     },
37479
37480     /**
37481      * Adds an {@link Roo.Element} object to the menu
37482      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37483      * @return {Roo.menu.Item} The menu item that was added
37484      */
37485     addElement : function(el){
37486         return this.addItem(new Roo.menu.BaseItem(el));
37487     },
37488
37489     /**
37490      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37491      * @param {Roo.menu.Item} item The menu item to add
37492      * @return {Roo.menu.Item} The menu item that was added
37493      */
37494     addItem : function(item){
37495         this.items.add(item);
37496         if(this.ul){
37497             var li = document.createElement("li");
37498             li.className = "x-menu-list-item";
37499             this.ul.dom.appendChild(li);
37500             item.render(li, this);
37501             this.delayAutoWidth();
37502         }
37503         return item;
37504     },
37505
37506     /**
37507      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37508      * @param {Object} config A MenuItem config object
37509      * @return {Roo.menu.Item} The menu item that was added
37510      */
37511     addMenuItem : function(config){
37512         if(!(config instanceof Roo.menu.Item)){
37513             if(typeof config.checked == "boolean"){ // must be check menu item config?
37514                 config = new Roo.menu.CheckItem(config);
37515             }else{
37516                 config = new Roo.menu.Item(config);
37517             }
37518         }
37519         return this.addItem(config);
37520     },
37521
37522     /**
37523      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37524      * @param {String} text The text to display in the menu item
37525      * @return {Roo.menu.Item} The menu item that was added
37526      */
37527     addText : function(text){
37528         return this.addItem(new Roo.menu.TextItem({ text : text }));
37529     },
37530
37531     /**
37532      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37533      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37534      * @param {Roo.menu.Item} item The menu item to add
37535      * @return {Roo.menu.Item} The menu item that was added
37536      */
37537     insert : function(index, item){
37538         this.items.insert(index, item);
37539         if(this.ul){
37540             var li = document.createElement("li");
37541             li.className = "x-menu-list-item";
37542             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37543             item.render(li, this);
37544             this.delayAutoWidth();
37545         }
37546         return item;
37547     },
37548
37549     /**
37550      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37551      * @param {Roo.menu.Item} item The menu item to remove
37552      */
37553     remove : function(item){
37554         this.items.removeKey(item.id);
37555         item.destroy();
37556     },
37557
37558     /**
37559      * Removes and destroys all items in the menu
37560      */
37561     removeAll : function(){
37562         var f;
37563         while(f = this.items.first()){
37564             this.remove(f);
37565         }
37566     }
37567 });
37568
37569 // MenuNav is a private utility class used internally by the Menu
37570 Roo.menu.MenuNav = function(menu){
37571     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37572     this.scope = this.menu = menu;
37573 };
37574
37575 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37576     doRelay : function(e, h){
37577         var k = e.getKey();
37578         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37579             this.menu.tryActivate(0, 1);
37580             return false;
37581         }
37582         return h.call(this.scope || this, e, this.menu);
37583     },
37584
37585     up : function(e, m){
37586         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37587             m.tryActivate(m.items.length-1, -1);
37588         }
37589     },
37590
37591     down : function(e, m){
37592         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37593             m.tryActivate(0, 1);
37594         }
37595     },
37596
37597     right : function(e, m){
37598         if(m.activeItem){
37599             m.activeItem.expandMenu(true);
37600         }
37601     },
37602
37603     left : function(e, m){
37604         m.hide();
37605         if(m.parentMenu && m.parentMenu.activeItem){
37606             m.parentMenu.activeItem.activate();
37607         }
37608     },
37609
37610     enter : function(e, m){
37611         if(m.activeItem){
37612             e.stopPropagation();
37613             m.activeItem.onClick(e);
37614             m.fireEvent("click", this, m.activeItem);
37615             return true;
37616         }
37617     }
37618 });/*
37619  * Based on:
37620  * Ext JS Library 1.1.1
37621  * Copyright(c) 2006-2007, Ext JS, LLC.
37622  *
37623  * Originally Released Under LGPL - original licence link has changed is not relivant.
37624  *
37625  * Fork - LGPL
37626  * <script type="text/javascript">
37627  */
37628  
37629 /**
37630  * @class Roo.menu.MenuMgr
37631  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37632  * @singleton
37633  */
37634 Roo.menu.MenuMgr = function(){
37635    var menus, active, groups = {}, attached = false, lastShow = new Date();
37636
37637    // private - called when first menu is created
37638    function init(){
37639        menus = {};
37640        active = new Roo.util.MixedCollection();
37641        Roo.get(document).addKeyListener(27, function(){
37642            if(active.length > 0){
37643                hideAll();
37644            }
37645        });
37646    }
37647
37648    // private
37649    function hideAll(){
37650        if(active && active.length > 0){
37651            var c = active.clone();
37652            c.each(function(m){
37653                m.hide();
37654            });
37655        }
37656    }
37657
37658    // private
37659    function onHide(m){
37660        active.remove(m);
37661        if(active.length < 1){
37662            Roo.get(document).un("mousedown", onMouseDown);
37663            attached = false;
37664        }
37665    }
37666
37667    // private
37668    function onShow(m){
37669        var last = active.last();
37670        lastShow = new Date();
37671        active.add(m);
37672        if(!attached){
37673            Roo.get(document).on("mousedown", onMouseDown);
37674            attached = true;
37675        }
37676        if(m.parentMenu){
37677           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37678           m.parentMenu.activeChild = m;
37679        }else if(last && last.isVisible()){
37680           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37681        }
37682    }
37683
37684    // private
37685    function onBeforeHide(m){
37686        if(m.activeChild){
37687            m.activeChild.hide();
37688        }
37689        if(m.autoHideTimer){
37690            clearTimeout(m.autoHideTimer);
37691            delete m.autoHideTimer;
37692        }
37693    }
37694
37695    // private
37696    function onBeforeShow(m){
37697        var pm = m.parentMenu;
37698        if(!pm && !m.allowOtherMenus){
37699            hideAll();
37700        }else if(pm && pm.activeChild && active != m){
37701            pm.activeChild.hide();
37702        }
37703    }
37704
37705    // private
37706    function onMouseDown(e){
37707        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37708            hideAll();
37709        }
37710    }
37711
37712    // private
37713    function onBeforeCheck(mi, state){
37714        if(state){
37715            var g = groups[mi.group];
37716            for(var i = 0, l = g.length; i < l; i++){
37717                if(g[i] != mi){
37718                    g[i].setChecked(false);
37719                }
37720            }
37721        }
37722    }
37723
37724    return {
37725
37726        /**
37727         * Hides all menus that are currently visible
37728         */
37729        hideAll : function(){
37730             hideAll();  
37731        },
37732
37733        // private
37734        register : function(menu){
37735            if(!menus){
37736                init();
37737            }
37738            menus[menu.id] = menu;
37739            menu.on("beforehide", onBeforeHide);
37740            menu.on("hide", onHide);
37741            menu.on("beforeshow", onBeforeShow);
37742            menu.on("show", onShow);
37743            var g = menu.group;
37744            if(g && menu.events["checkchange"]){
37745                if(!groups[g]){
37746                    groups[g] = [];
37747                }
37748                groups[g].push(menu);
37749                menu.on("checkchange", onCheck);
37750            }
37751        },
37752
37753         /**
37754          * Returns a {@link Roo.menu.Menu} object
37755          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37756          * be used to generate and return a new Menu instance.
37757          */
37758        get : function(menu){
37759            if(typeof menu == "string"){ // menu id
37760                return menus[menu];
37761            }else if(menu.events){  // menu instance
37762                return menu;
37763            }else if(typeof menu.length == 'number'){ // array of menu items?
37764                return new Roo.menu.Menu({items:menu});
37765            }else{ // otherwise, must be a config
37766                return new Roo.menu.Menu(menu);
37767            }
37768        },
37769
37770        // private
37771        unregister : function(menu){
37772            delete menus[menu.id];
37773            menu.un("beforehide", onBeforeHide);
37774            menu.un("hide", onHide);
37775            menu.un("beforeshow", onBeforeShow);
37776            menu.un("show", onShow);
37777            var g = menu.group;
37778            if(g && menu.events["checkchange"]){
37779                groups[g].remove(menu);
37780                menu.un("checkchange", onCheck);
37781            }
37782        },
37783
37784        // private
37785        registerCheckable : function(menuItem){
37786            var g = menuItem.group;
37787            if(g){
37788                if(!groups[g]){
37789                    groups[g] = [];
37790                }
37791                groups[g].push(menuItem);
37792                menuItem.on("beforecheckchange", onBeforeCheck);
37793            }
37794        },
37795
37796        // private
37797        unregisterCheckable : function(menuItem){
37798            var g = menuItem.group;
37799            if(g){
37800                groups[g].remove(menuItem);
37801                menuItem.un("beforecheckchange", onBeforeCheck);
37802            }
37803        }
37804    };
37805 }();/*
37806  * Based on:
37807  * Ext JS Library 1.1.1
37808  * Copyright(c) 2006-2007, Ext JS, LLC.
37809  *
37810  * Originally Released Under LGPL - original licence link has changed is not relivant.
37811  *
37812  * Fork - LGPL
37813  * <script type="text/javascript">
37814  */
37815  
37816
37817 /**
37818  * @class Roo.menu.BaseItem
37819  * @extends Roo.Component
37820  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37821  * management and base configuration options shared by all menu components.
37822  * @constructor
37823  * Creates a new BaseItem
37824  * @param {Object} config Configuration options
37825  */
37826 Roo.menu.BaseItem = function(config){
37827     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37828
37829     this.addEvents({
37830         /**
37831          * @event click
37832          * Fires when this item is clicked
37833          * @param {Roo.menu.BaseItem} this
37834          * @param {Roo.EventObject} e
37835          */
37836         click: true,
37837         /**
37838          * @event activate
37839          * Fires when this item is activated
37840          * @param {Roo.menu.BaseItem} this
37841          */
37842         activate : true,
37843         /**
37844          * @event deactivate
37845          * Fires when this item is deactivated
37846          * @param {Roo.menu.BaseItem} this
37847          */
37848         deactivate : true
37849     });
37850
37851     if(this.handler){
37852         this.on("click", this.handler, this.scope, true);
37853     }
37854 };
37855
37856 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37857     /**
37858      * @cfg {Function} handler
37859      * A function that will handle the click event of this menu item (defaults to undefined)
37860      */
37861     /**
37862      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37863      */
37864     canActivate : false,
37865     
37866      /**
37867      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37868      */
37869     hidden: false,
37870     
37871     /**
37872      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37873      */
37874     activeClass : "x-menu-item-active",
37875     /**
37876      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37877      */
37878     hideOnClick : true,
37879     /**
37880      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37881      */
37882     hideDelay : 100,
37883
37884     // private
37885     ctype: "Roo.menu.BaseItem",
37886
37887     // private
37888     actionMode : "container",
37889
37890     // private
37891     render : function(container, parentMenu){
37892         this.parentMenu = parentMenu;
37893         Roo.menu.BaseItem.superclass.render.call(this, container);
37894         this.container.menuItemId = this.id;
37895     },
37896
37897     // private
37898     onRender : function(container, position){
37899         this.el = Roo.get(this.el);
37900         container.dom.appendChild(this.el.dom);
37901     },
37902
37903     // private
37904     onClick : function(e){
37905         if(!this.disabled && this.fireEvent("click", this, e) !== false
37906                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37907             this.handleClick(e);
37908         }else{
37909             e.stopEvent();
37910         }
37911     },
37912
37913     // private
37914     activate : function(){
37915         if(this.disabled){
37916             return false;
37917         }
37918         var li = this.container;
37919         li.addClass(this.activeClass);
37920         this.region = li.getRegion().adjust(2, 2, -2, -2);
37921         this.fireEvent("activate", this);
37922         return true;
37923     },
37924
37925     // private
37926     deactivate : function(){
37927         this.container.removeClass(this.activeClass);
37928         this.fireEvent("deactivate", this);
37929     },
37930
37931     // private
37932     shouldDeactivate : function(e){
37933         return !this.region || !this.region.contains(e.getPoint());
37934     },
37935
37936     // private
37937     handleClick : function(e){
37938         if(this.hideOnClick){
37939             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37940         }
37941     },
37942
37943     // private
37944     expandMenu : function(autoActivate){
37945         // do nothing
37946     },
37947
37948     // private
37949     hideMenu : function(){
37950         // do nothing
37951     }
37952 });/*
37953  * Based on:
37954  * Ext JS Library 1.1.1
37955  * Copyright(c) 2006-2007, Ext JS, LLC.
37956  *
37957  * Originally Released Under LGPL - original licence link has changed is not relivant.
37958  *
37959  * Fork - LGPL
37960  * <script type="text/javascript">
37961  */
37962  
37963 /**
37964  * @class Roo.menu.Adapter
37965  * @extends Roo.menu.BaseItem
37966  * 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.
37967  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
37968  * @constructor
37969  * Creates a new Adapter
37970  * @param {Object} config Configuration options
37971  */
37972 Roo.menu.Adapter = function(component, config){
37973     Roo.menu.Adapter.superclass.constructor.call(this, config);
37974     this.component = component;
37975 };
37976 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
37977     // private
37978     canActivate : true,
37979
37980     // private
37981     onRender : function(container, position){
37982         this.component.render(container);
37983         this.el = this.component.getEl();
37984     },
37985
37986     // private
37987     activate : function(){
37988         if(this.disabled){
37989             return false;
37990         }
37991         this.component.focus();
37992         this.fireEvent("activate", this);
37993         return true;
37994     },
37995
37996     // private
37997     deactivate : function(){
37998         this.fireEvent("deactivate", this);
37999     },
38000
38001     // private
38002     disable : function(){
38003         this.component.disable();
38004         Roo.menu.Adapter.superclass.disable.call(this);
38005     },
38006
38007     // private
38008     enable : function(){
38009         this.component.enable();
38010         Roo.menu.Adapter.superclass.enable.call(this);
38011     }
38012 });/*
38013  * Based on:
38014  * Ext JS Library 1.1.1
38015  * Copyright(c) 2006-2007, Ext JS, LLC.
38016  *
38017  * Originally Released Under LGPL - original licence link has changed is not relivant.
38018  *
38019  * Fork - LGPL
38020  * <script type="text/javascript">
38021  */
38022
38023 /**
38024  * @class Roo.menu.TextItem
38025  * @extends Roo.menu.BaseItem
38026  * Adds a static text string to a menu, usually used as either a heading or group separator.
38027  * Note: old style constructor with text is still supported.
38028  * 
38029  * @constructor
38030  * Creates a new TextItem
38031  * @param {Object} cfg Configuration
38032  */
38033 Roo.menu.TextItem = function(cfg){
38034     if (typeof(cfg) == 'string') {
38035         this.text = cfg;
38036     } else {
38037         Roo.apply(this,cfg);
38038     }
38039     
38040     Roo.menu.TextItem.superclass.constructor.call(this);
38041 };
38042
38043 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38044     /**
38045      * @cfg {Boolean} text Text to show on item.
38046      */
38047     text : '',
38048     
38049     /**
38050      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38051      */
38052     hideOnClick : false,
38053     /**
38054      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38055      */
38056     itemCls : "x-menu-text",
38057
38058     // private
38059     onRender : function(){
38060         var s = document.createElement("span");
38061         s.className = this.itemCls;
38062         s.innerHTML = this.text;
38063         this.el = s;
38064         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38065     }
38066 });/*
38067  * Based on:
38068  * Ext JS Library 1.1.1
38069  * Copyright(c) 2006-2007, Ext JS, LLC.
38070  *
38071  * Originally Released Under LGPL - original licence link has changed is not relivant.
38072  *
38073  * Fork - LGPL
38074  * <script type="text/javascript">
38075  */
38076
38077 /**
38078  * @class Roo.menu.Separator
38079  * @extends Roo.menu.BaseItem
38080  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38081  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38082  * @constructor
38083  * @param {Object} config Configuration options
38084  */
38085 Roo.menu.Separator = function(config){
38086     Roo.menu.Separator.superclass.constructor.call(this, config);
38087 };
38088
38089 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38090     /**
38091      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38092      */
38093     itemCls : "x-menu-sep",
38094     /**
38095      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38096      */
38097     hideOnClick : false,
38098
38099     // private
38100     onRender : function(li){
38101         var s = document.createElement("span");
38102         s.className = this.itemCls;
38103         s.innerHTML = "&#160;";
38104         this.el = s;
38105         li.addClass("x-menu-sep-li");
38106         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38107     }
38108 });/*
38109  * Based on:
38110  * Ext JS Library 1.1.1
38111  * Copyright(c) 2006-2007, Ext JS, LLC.
38112  *
38113  * Originally Released Under LGPL - original licence link has changed is not relivant.
38114  *
38115  * Fork - LGPL
38116  * <script type="text/javascript">
38117  */
38118 /**
38119  * @class Roo.menu.Item
38120  * @extends Roo.menu.BaseItem
38121  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38122  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38123  * activation and click handling.
38124  * @constructor
38125  * Creates a new Item
38126  * @param {Object} config Configuration options
38127  */
38128 Roo.menu.Item = function(config){
38129     Roo.menu.Item.superclass.constructor.call(this, config);
38130     if(this.menu){
38131         this.menu = Roo.menu.MenuMgr.get(this.menu);
38132     }
38133 };
38134 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38135     
38136     /**
38137      * @cfg {String} text
38138      * The text to show on the menu item.
38139      */
38140     text: '',
38141      /**
38142      * @cfg {String} HTML to render in menu
38143      * The text to show on the menu item (HTML version).
38144      */
38145     html: '',
38146     /**
38147      * @cfg {String} icon
38148      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38149      */
38150     icon: undefined,
38151     /**
38152      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38153      */
38154     itemCls : "x-menu-item",
38155     /**
38156      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38157      */
38158     canActivate : true,
38159     /**
38160      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38161      */
38162     showDelay: 200,
38163     // doc'd in BaseItem
38164     hideDelay: 200,
38165
38166     // private
38167     ctype: "Roo.menu.Item",
38168     
38169     // private
38170     onRender : function(container, position){
38171         var el = document.createElement("a");
38172         el.hideFocus = true;
38173         el.unselectable = "on";
38174         el.href = this.href || "#";
38175         if(this.hrefTarget){
38176             el.target = this.hrefTarget;
38177         }
38178         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38179         
38180         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38181         
38182         el.innerHTML = String.format(
38183                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38184                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38185         this.el = el;
38186         Roo.menu.Item.superclass.onRender.call(this, container, position);
38187     },
38188
38189     /**
38190      * Sets the text to display in this menu item
38191      * @param {String} text The text to display
38192      * @param {Boolean} isHTML true to indicate text is pure html.
38193      */
38194     setText : function(text, isHTML){
38195         if (isHTML) {
38196             this.html = text;
38197         } else {
38198             this.text = text;
38199             this.html = '';
38200         }
38201         if(this.rendered){
38202             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38203      
38204             this.el.update(String.format(
38205                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38206                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38207             this.parentMenu.autoWidth();
38208         }
38209     },
38210
38211     // private
38212     handleClick : function(e){
38213         if(!this.href){ // if no link defined, stop the event automatically
38214             e.stopEvent();
38215         }
38216         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38217     },
38218
38219     // private
38220     activate : function(autoExpand){
38221         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38222             this.focus();
38223             if(autoExpand){
38224                 this.expandMenu();
38225             }
38226         }
38227         return true;
38228     },
38229
38230     // private
38231     shouldDeactivate : function(e){
38232         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38233             if(this.menu && this.menu.isVisible()){
38234                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38235             }
38236             return true;
38237         }
38238         return false;
38239     },
38240
38241     // private
38242     deactivate : function(){
38243         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38244         this.hideMenu();
38245     },
38246
38247     // private
38248     expandMenu : function(autoActivate){
38249         if(!this.disabled && this.menu){
38250             clearTimeout(this.hideTimer);
38251             delete this.hideTimer;
38252             if(!this.menu.isVisible() && !this.showTimer){
38253                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38254             }else if (this.menu.isVisible() && autoActivate){
38255                 this.menu.tryActivate(0, 1);
38256             }
38257         }
38258     },
38259
38260     // private
38261     deferExpand : function(autoActivate){
38262         delete this.showTimer;
38263         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38264         if(autoActivate){
38265             this.menu.tryActivate(0, 1);
38266         }
38267     },
38268
38269     // private
38270     hideMenu : function(){
38271         clearTimeout(this.showTimer);
38272         delete this.showTimer;
38273         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38274             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38275         }
38276     },
38277
38278     // private
38279     deferHide : function(){
38280         delete this.hideTimer;
38281         this.menu.hide();
38282     }
38283 });/*
38284  * Based on:
38285  * Ext JS Library 1.1.1
38286  * Copyright(c) 2006-2007, Ext JS, LLC.
38287  *
38288  * Originally Released Under LGPL - original licence link has changed is not relivant.
38289  *
38290  * Fork - LGPL
38291  * <script type="text/javascript">
38292  */
38293  
38294 /**
38295  * @class Roo.menu.CheckItem
38296  * @extends Roo.menu.Item
38297  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38298  * @constructor
38299  * Creates a new CheckItem
38300  * @param {Object} config Configuration options
38301  */
38302 Roo.menu.CheckItem = function(config){
38303     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38304     this.addEvents({
38305         /**
38306          * @event beforecheckchange
38307          * Fires before the checked value is set, providing an opportunity to cancel if needed
38308          * @param {Roo.menu.CheckItem} this
38309          * @param {Boolean} checked The new checked value that will be set
38310          */
38311         "beforecheckchange" : true,
38312         /**
38313          * @event checkchange
38314          * Fires after the checked value has been set
38315          * @param {Roo.menu.CheckItem} this
38316          * @param {Boolean} checked The checked value that was set
38317          */
38318         "checkchange" : true
38319     });
38320     if(this.checkHandler){
38321         this.on('checkchange', this.checkHandler, this.scope);
38322     }
38323 };
38324 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38325     /**
38326      * @cfg {String} group
38327      * All check items with the same group name will automatically be grouped into a single-select
38328      * radio button group (defaults to '')
38329      */
38330     /**
38331      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38332      */
38333     itemCls : "x-menu-item x-menu-check-item",
38334     /**
38335      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38336      */
38337     groupClass : "x-menu-group-item",
38338
38339     /**
38340      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38341      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38342      * initialized with checked = true will be rendered as checked.
38343      */
38344     checked: false,
38345
38346     // private
38347     ctype: "Roo.menu.CheckItem",
38348
38349     // private
38350     onRender : function(c){
38351         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38352         if(this.group){
38353             this.el.addClass(this.groupClass);
38354         }
38355         Roo.menu.MenuMgr.registerCheckable(this);
38356         if(this.checked){
38357             this.checked = false;
38358             this.setChecked(true, true);
38359         }
38360     },
38361
38362     // private
38363     destroy : function(){
38364         if(this.rendered){
38365             Roo.menu.MenuMgr.unregisterCheckable(this);
38366         }
38367         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38368     },
38369
38370     /**
38371      * Set the checked state of this item
38372      * @param {Boolean} checked The new checked value
38373      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38374      */
38375     setChecked : function(state, suppressEvent){
38376         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38377             if(this.container){
38378                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38379             }
38380             this.checked = state;
38381             if(suppressEvent !== true){
38382                 this.fireEvent("checkchange", this, state);
38383             }
38384         }
38385     },
38386
38387     // private
38388     handleClick : function(e){
38389        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38390            this.setChecked(!this.checked);
38391        }
38392        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38393     }
38394 });/*
38395  * Based on:
38396  * Ext JS Library 1.1.1
38397  * Copyright(c) 2006-2007, Ext JS, LLC.
38398  *
38399  * Originally Released Under LGPL - original licence link has changed is not relivant.
38400  *
38401  * Fork - LGPL
38402  * <script type="text/javascript">
38403  */
38404  
38405 /**
38406  * @class Roo.menu.DateItem
38407  * @extends Roo.menu.Adapter
38408  * A menu item that wraps the {@link Roo.DatPicker} component.
38409  * @constructor
38410  * Creates a new DateItem
38411  * @param {Object} config Configuration options
38412  */
38413 Roo.menu.DateItem = function(config){
38414     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38415     /** The Roo.DatePicker object @type Roo.DatePicker */
38416     this.picker = this.component;
38417     this.addEvents({select: true});
38418     
38419     this.picker.on("render", function(picker){
38420         picker.getEl().swallowEvent("click");
38421         picker.container.addClass("x-menu-date-item");
38422     });
38423
38424     this.picker.on("select", this.onSelect, this);
38425 };
38426
38427 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38428     // private
38429     onSelect : function(picker, date){
38430         this.fireEvent("select", this, date, picker);
38431         Roo.menu.DateItem.superclass.handleClick.call(this);
38432     }
38433 });/*
38434  * Based on:
38435  * Ext JS Library 1.1.1
38436  * Copyright(c) 2006-2007, Ext JS, LLC.
38437  *
38438  * Originally Released Under LGPL - original licence link has changed is not relivant.
38439  *
38440  * Fork - LGPL
38441  * <script type="text/javascript">
38442  */
38443  
38444 /**
38445  * @class Roo.menu.ColorItem
38446  * @extends Roo.menu.Adapter
38447  * A menu item that wraps the {@link Roo.ColorPalette} component.
38448  * @constructor
38449  * Creates a new ColorItem
38450  * @param {Object} config Configuration options
38451  */
38452 Roo.menu.ColorItem = function(config){
38453     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38454     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38455     this.palette = this.component;
38456     this.relayEvents(this.palette, ["select"]);
38457     if(this.selectHandler){
38458         this.on('select', this.selectHandler, this.scope);
38459     }
38460 };
38461 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38462  * Based on:
38463  * Ext JS Library 1.1.1
38464  * Copyright(c) 2006-2007, Ext JS, LLC.
38465  *
38466  * Originally Released Under LGPL - original licence link has changed is not relivant.
38467  *
38468  * Fork - LGPL
38469  * <script type="text/javascript">
38470  */
38471  
38472
38473 /**
38474  * @class Roo.menu.DateMenu
38475  * @extends Roo.menu.Menu
38476  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38477  * @constructor
38478  * Creates a new DateMenu
38479  * @param {Object} config Configuration options
38480  */
38481 Roo.menu.DateMenu = function(config){
38482     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38483     this.plain = true;
38484     var di = new Roo.menu.DateItem(config);
38485     this.add(di);
38486     /**
38487      * The {@link Roo.DatePicker} instance for this DateMenu
38488      * @type DatePicker
38489      */
38490     this.picker = di.picker;
38491     /**
38492      * @event select
38493      * @param {DatePicker} picker
38494      * @param {Date} date
38495      */
38496     this.relayEvents(di, ["select"]);
38497     this.on('beforeshow', function(){
38498         if(this.picker){
38499             this.picker.hideMonthPicker(false);
38500         }
38501     }, this);
38502 };
38503 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38504     cls:'x-date-menu'
38505 });/*
38506  * Based on:
38507  * Ext JS Library 1.1.1
38508  * Copyright(c) 2006-2007, Ext JS, LLC.
38509  *
38510  * Originally Released Under LGPL - original licence link has changed is not relivant.
38511  *
38512  * Fork - LGPL
38513  * <script type="text/javascript">
38514  */
38515  
38516
38517 /**
38518  * @class Roo.menu.ColorMenu
38519  * @extends Roo.menu.Menu
38520  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38521  * @constructor
38522  * Creates a new ColorMenu
38523  * @param {Object} config Configuration options
38524  */
38525 Roo.menu.ColorMenu = function(config){
38526     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38527     this.plain = true;
38528     var ci = new Roo.menu.ColorItem(config);
38529     this.add(ci);
38530     /**
38531      * The {@link Roo.ColorPalette} instance for this ColorMenu
38532      * @type ColorPalette
38533      */
38534     this.palette = ci.palette;
38535     /**
38536      * @event select
38537      * @param {ColorPalette} palette
38538      * @param {String} color
38539      */
38540     this.relayEvents(ci, ["select"]);
38541 };
38542 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38543  * Based on:
38544  * Ext JS Library 1.1.1
38545  * Copyright(c) 2006-2007, Ext JS, LLC.
38546  *
38547  * Originally Released Under LGPL - original licence link has changed is not relivant.
38548  *
38549  * Fork - LGPL
38550  * <script type="text/javascript">
38551  */
38552  
38553 /**
38554  * @class Roo.form.Field
38555  * @extends Roo.BoxComponent
38556  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38557  * @constructor
38558  * Creates a new Field
38559  * @param {Object} config Configuration options
38560  */
38561 Roo.form.Field = function(config){
38562     Roo.form.Field.superclass.constructor.call(this, config);
38563 };
38564
38565 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38566     /**
38567      * @cfg {String} fieldLabel Label to use when rendering a form.
38568      */
38569        /**
38570      * @cfg {String} qtip Mouse over tip
38571      */
38572      
38573     /**
38574      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38575      */
38576     invalidClass : "x-form-invalid",
38577     /**
38578      * @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")
38579      */
38580     invalidText : "The value in this field is invalid",
38581     /**
38582      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38583      */
38584     focusClass : "x-form-focus",
38585     /**
38586      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38587       automatic validation (defaults to "keyup").
38588      */
38589     validationEvent : "keyup",
38590     /**
38591      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38592      */
38593     validateOnBlur : true,
38594     /**
38595      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38596      */
38597     validationDelay : 250,
38598     /**
38599      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38600      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38601      */
38602     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38603     /**
38604      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38605      */
38606     fieldClass : "x-form-field",
38607     /**
38608      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38609      *<pre>
38610 Value         Description
38611 -----------   ----------------------------------------------------------------------
38612 qtip          Display a quick tip when the user hovers over the field
38613 title         Display a default browser title attribute popup
38614 under         Add a block div beneath the field containing the error text
38615 side          Add an error icon to the right of the field with a popup on hover
38616 [element id]  Add the error text directly to the innerHTML of the specified element
38617 </pre>
38618      */
38619     msgTarget : 'qtip',
38620     /**
38621      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38622      */
38623     msgFx : 'normal',
38624
38625     /**
38626      * @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.
38627      */
38628     readOnly : false,
38629
38630     /**
38631      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38632      */
38633     disabled : false,
38634
38635     /**
38636      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38637      */
38638     inputType : undefined,
38639     
38640     /**
38641      * @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).
38642          */
38643         tabIndex : undefined,
38644         
38645     // private
38646     isFormField : true,
38647
38648     // private
38649     hasFocus : false,
38650     /**
38651      * @property {Roo.Element} fieldEl
38652      * Element Containing the rendered Field (with label etc.)
38653      */
38654     /**
38655      * @cfg {Mixed} value A value to initialize this field with.
38656      */
38657     value : undefined,
38658
38659     /**
38660      * @cfg {String} name The field's HTML name attribute.
38661      */
38662     /**
38663      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38664      */
38665     // private
38666     loadedValue : false,
38667      
38668      
38669         // private ??
38670         initComponent : function(){
38671         Roo.form.Field.superclass.initComponent.call(this);
38672         this.addEvents({
38673             /**
38674              * @event focus
38675              * Fires when this field receives input focus.
38676              * @param {Roo.form.Field} this
38677              */
38678             focus : true,
38679             /**
38680              * @event blur
38681              * Fires when this field loses input focus.
38682              * @param {Roo.form.Field} this
38683              */
38684             blur : true,
38685             /**
38686              * @event specialkey
38687              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38688              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38689              * @param {Roo.form.Field} this
38690              * @param {Roo.EventObject} e The event object
38691              */
38692             specialkey : true,
38693             /**
38694              * @event change
38695              * Fires just before the field blurs if the field value has changed.
38696              * @param {Roo.form.Field} this
38697              * @param {Mixed} newValue The new value
38698              * @param {Mixed} oldValue The original value
38699              */
38700             change : true,
38701             /**
38702              * @event invalid
38703              * Fires after the field has been marked as invalid.
38704              * @param {Roo.form.Field} this
38705              * @param {String} msg The validation message
38706              */
38707             invalid : true,
38708             /**
38709              * @event valid
38710              * Fires after the field has been validated with no errors.
38711              * @param {Roo.form.Field} this
38712              */
38713             valid : true,
38714              /**
38715              * @event keyup
38716              * Fires after the key up
38717              * @param {Roo.form.Field} this
38718              * @param {Roo.EventObject}  e The event Object
38719              */
38720             keyup : true
38721         });
38722     },
38723
38724     /**
38725      * Returns the name attribute of the field if available
38726      * @return {String} name The field name
38727      */
38728     getName: function(){
38729          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38730     },
38731
38732     // private
38733     onRender : function(ct, position){
38734         Roo.form.Field.superclass.onRender.call(this, ct, position);
38735         if(!this.el){
38736             var cfg = this.getAutoCreate();
38737             if(!cfg.name){
38738                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38739             }
38740             if (!cfg.name.length) {
38741                 delete cfg.name;
38742             }
38743             if(this.inputType){
38744                 cfg.type = this.inputType;
38745             }
38746             this.el = ct.createChild(cfg, position);
38747         }
38748         var type = this.el.dom.type;
38749         if(type){
38750             if(type == 'password'){
38751                 type = 'text';
38752             }
38753             this.el.addClass('x-form-'+type);
38754         }
38755         if(this.readOnly){
38756             this.el.dom.readOnly = true;
38757         }
38758         if(this.tabIndex !== undefined){
38759             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38760         }
38761
38762         this.el.addClass([this.fieldClass, this.cls]);
38763         this.initValue();
38764     },
38765
38766     /**
38767      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38768      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38769      * @return {Roo.form.Field} this
38770      */
38771     applyTo : function(target){
38772         this.allowDomMove = false;
38773         this.el = Roo.get(target);
38774         this.render(this.el.dom.parentNode);
38775         return this;
38776     },
38777
38778     // private
38779     initValue : function(){
38780         if(this.value !== undefined){
38781             this.setValue(this.value);
38782         }else if(this.el.dom.value.length > 0){
38783             this.setValue(this.el.dom.value);
38784         }
38785     },
38786
38787     /**
38788      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38789      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38790      */
38791     isDirty : function() {
38792         if(this.disabled) {
38793             return false;
38794         }
38795         return String(this.getValue()) !== String(this.originalValue);
38796     },
38797
38798     /**
38799      * stores the current value in loadedValue
38800      */
38801     resetHasChanged : function()
38802     {
38803         this.loadedValue = String(this.getValue());
38804     },
38805     /**
38806      * checks the current value against the 'loaded' value.
38807      * Note - will return false if 'resetHasChanged' has not been called first.
38808      */
38809     hasChanged : function()
38810     {
38811         if(this.disabled || this.readOnly) {
38812             return false;
38813         }
38814         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38815     },
38816     
38817     
38818     
38819     // private
38820     afterRender : function(){
38821         Roo.form.Field.superclass.afterRender.call(this);
38822         this.initEvents();
38823     },
38824
38825     // private
38826     fireKey : function(e){
38827         //Roo.log('field ' + e.getKey());
38828         if(e.isNavKeyPress()){
38829             this.fireEvent("specialkey", this, e);
38830         }
38831     },
38832
38833     /**
38834      * Resets the current field value to the originally loaded value and clears any validation messages
38835      */
38836     reset : function(){
38837         this.setValue(this.resetValue);
38838         this.clearInvalid();
38839     },
38840
38841     // private
38842     initEvents : function(){
38843         // safari killled keypress - so keydown is now used..
38844         this.el.on("keydown" , this.fireKey,  this);
38845         this.el.on("focus", this.onFocus,  this);
38846         this.el.on("blur", this.onBlur,  this);
38847         this.el.relayEvent('keyup', this);
38848
38849         // reference to original value for reset
38850         this.originalValue = this.getValue();
38851         this.resetValue =  this.getValue();
38852     },
38853
38854     // private
38855     onFocus : function(){
38856         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38857             this.el.addClass(this.focusClass);
38858         }
38859         if(!this.hasFocus){
38860             this.hasFocus = true;
38861             this.startValue = this.getValue();
38862             this.fireEvent("focus", this);
38863         }
38864     },
38865
38866     beforeBlur : Roo.emptyFn,
38867
38868     // private
38869     onBlur : function(){
38870         this.beforeBlur();
38871         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38872             this.el.removeClass(this.focusClass);
38873         }
38874         this.hasFocus = false;
38875         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38876             this.validate();
38877         }
38878         var v = this.getValue();
38879         if(String(v) !== String(this.startValue)){
38880             this.fireEvent('change', this, v, this.startValue);
38881         }
38882         this.fireEvent("blur", this);
38883     },
38884
38885     /**
38886      * Returns whether or not the field value is currently valid
38887      * @param {Boolean} preventMark True to disable marking the field invalid
38888      * @return {Boolean} True if the value is valid, else false
38889      */
38890     isValid : function(preventMark){
38891         if(this.disabled){
38892             return true;
38893         }
38894         var restore = this.preventMark;
38895         this.preventMark = preventMark === true;
38896         var v = this.validateValue(this.processValue(this.getRawValue()));
38897         this.preventMark = restore;
38898         return v;
38899     },
38900
38901     /**
38902      * Validates the field value
38903      * @return {Boolean} True if the value is valid, else false
38904      */
38905     validate : function(){
38906         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38907             this.clearInvalid();
38908             return true;
38909         }
38910         return false;
38911     },
38912
38913     processValue : function(value){
38914         return value;
38915     },
38916
38917     // private
38918     // Subclasses should provide the validation implementation by overriding this
38919     validateValue : function(value){
38920         return true;
38921     },
38922
38923     /**
38924      * Mark this field as invalid
38925      * @param {String} msg The validation message
38926      */
38927     markInvalid : function(msg){
38928         if(!this.rendered || this.preventMark){ // not rendered
38929             return;
38930         }
38931         
38932         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38933         
38934         obj.el.addClass(this.invalidClass);
38935         msg = msg || this.invalidText;
38936         switch(this.msgTarget){
38937             case 'qtip':
38938                 obj.el.dom.qtip = msg;
38939                 obj.el.dom.qclass = 'x-form-invalid-tip';
38940                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38941                     Roo.QuickTips.enable();
38942                 }
38943                 break;
38944             case 'title':
38945                 this.el.dom.title = msg;
38946                 break;
38947             case 'under':
38948                 if(!this.errorEl){
38949                     var elp = this.el.findParent('.x-form-element', 5, true);
38950                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38951                     this.errorEl.setWidth(elp.getWidth(true)-20);
38952                 }
38953                 this.errorEl.update(msg);
38954                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38955                 break;
38956             case 'side':
38957                 if(!this.errorIcon){
38958                     var elp = this.el.findParent('.x-form-element', 5, true);
38959                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38960                 }
38961                 this.alignErrorIcon();
38962                 this.errorIcon.dom.qtip = msg;
38963                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38964                 this.errorIcon.show();
38965                 this.on('resize', this.alignErrorIcon, this);
38966                 break;
38967             default:
38968                 var t = Roo.getDom(this.msgTarget);
38969                 t.innerHTML = msg;
38970                 t.style.display = this.msgDisplay;
38971                 break;
38972         }
38973         this.fireEvent('invalid', this, msg);
38974     },
38975
38976     // private
38977     alignErrorIcon : function(){
38978         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
38979     },
38980
38981     /**
38982      * Clear any invalid styles/messages for this field
38983      */
38984     clearInvalid : function(){
38985         if(!this.rendered || this.preventMark){ // not rendered
38986             return;
38987         }
38988         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38989         
38990         obj.el.removeClass(this.invalidClass);
38991         switch(this.msgTarget){
38992             case 'qtip':
38993                 obj.el.dom.qtip = '';
38994                 break;
38995             case 'title':
38996                 this.el.dom.title = '';
38997                 break;
38998             case 'under':
38999                 if(this.errorEl){
39000                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39001                 }
39002                 break;
39003             case 'side':
39004                 if(this.errorIcon){
39005                     this.errorIcon.dom.qtip = '';
39006                     this.errorIcon.hide();
39007                     this.un('resize', this.alignErrorIcon, this);
39008                 }
39009                 break;
39010             default:
39011                 var t = Roo.getDom(this.msgTarget);
39012                 t.innerHTML = '';
39013                 t.style.display = 'none';
39014                 break;
39015         }
39016         this.fireEvent('valid', this);
39017     },
39018
39019     /**
39020      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39021      * @return {Mixed} value The field value
39022      */
39023     getRawValue : function(){
39024         var v = this.el.getValue();
39025         
39026         return v;
39027     },
39028
39029     /**
39030      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39031      * @return {Mixed} value The field value
39032      */
39033     getValue : function(){
39034         var v = this.el.getValue();
39035          
39036         return v;
39037     },
39038
39039     /**
39040      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39041      * @param {Mixed} value The value to set
39042      */
39043     setRawValue : function(v){
39044         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39045     },
39046
39047     /**
39048      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39049      * @param {Mixed} value The value to set
39050      */
39051     setValue : function(v){
39052         this.value = v;
39053         if(this.rendered){
39054             this.el.dom.value = (v === null || v === undefined ? '' : v);
39055              this.validate();
39056         }
39057     },
39058
39059     adjustSize : function(w, h){
39060         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39061         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39062         return s;
39063     },
39064
39065     adjustWidth : function(tag, w){
39066         tag = tag.toLowerCase();
39067         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39068             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39069                 if(tag == 'input'){
39070                     return w + 2;
39071                 }
39072                 if(tag == 'textarea'){
39073                     return w-2;
39074                 }
39075             }else if(Roo.isOpera){
39076                 if(tag == 'input'){
39077                     return w + 2;
39078                 }
39079                 if(tag == 'textarea'){
39080                     return w-2;
39081                 }
39082             }
39083         }
39084         return w;
39085     }
39086 });
39087
39088
39089 // anything other than normal should be considered experimental
39090 Roo.form.Field.msgFx = {
39091     normal : {
39092         show: function(msgEl, f){
39093             msgEl.setDisplayed('block');
39094         },
39095
39096         hide : function(msgEl, f){
39097             msgEl.setDisplayed(false).update('');
39098         }
39099     },
39100
39101     slide : {
39102         show: function(msgEl, f){
39103             msgEl.slideIn('t', {stopFx:true});
39104         },
39105
39106         hide : function(msgEl, f){
39107             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39108         }
39109     },
39110
39111     slideRight : {
39112         show: function(msgEl, f){
39113             msgEl.fixDisplay();
39114             msgEl.alignTo(f.el, 'tl-tr');
39115             msgEl.slideIn('l', {stopFx:true});
39116         },
39117
39118         hide : function(msgEl, f){
39119             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39120         }
39121     }
39122 };/*
39123  * Based on:
39124  * Ext JS Library 1.1.1
39125  * Copyright(c) 2006-2007, Ext JS, LLC.
39126  *
39127  * Originally Released Under LGPL - original licence link has changed is not relivant.
39128  *
39129  * Fork - LGPL
39130  * <script type="text/javascript">
39131  */
39132  
39133
39134 /**
39135  * @class Roo.form.TextField
39136  * @extends Roo.form.Field
39137  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39138  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39139  * @constructor
39140  * Creates a new TextField
39141  * @param {Object} config Configuration options
39142  */
39143 Roo.form.TextField = function(config){
39144     Roo.form.TextField.superclass.constructor.call(this, config);
39145     this.addEvents({
39146         /**
39147          * @event autosize
39148          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39149          * according to the default logic, but this event provides a hook for the developer to apply additional
39150          * logic at runtime to resize the field if needed.
39151              * @param {Roo.form.Field} this This text field
39152              * @param {Number} width The new field width
39153              */
39154         autosize : true
39155     });
39156 };
39157
39158 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39159     /**
39160      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39161      */
39162     grow : false,
39163     /**
39164      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39165      */
39166     growMin : 30,
39167     /**
39168      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39169      */
39170     growMax : 800,
39171     /**
39172      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39173      */
39174     vtype : null,
39175     /**
39176      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39177      */
39178     maskRe : null,
39179     /**
39180      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39181      */
39182     disableKeyFilter : false,
39183     /**
39184      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39185      */
39186     allowBlank : true,
39187     /**
39188      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39189      */
39190     minLength : 0,
39191     /**
39192      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39193      */
39194     maxLength : Number.MAX_VALUE,
39195     /**
39196      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39197      */
39198     minLengthText : "The minimum length for this field is {0}",
39199     /**
39200      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39201      */
39202     maxLengthText : "The maximum length for this field is {0}",
39203     /**
39204      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39205      */
39206     selectOnFocus : false,
39207     /**
39208      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39209      */
39210     blankText : "This field is required",
39211     /**
39212      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39213      * If available, this function will be called only after the basic validators all return true, and will be passed the
39214      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39215      */
39216     validator : null,
39217     /**
39218      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39219      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39220      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39221      */
39222     regex : null,
39223     /**
39224      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39225      */
39226     regexText : "",
39227     /**
39228      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39229      */
39230     emptyText : null,
39231    
39232
39233     // private
39234     initEvents : function()
39235     {
39236         if (this.emptyText) {
39237             this.el.attr('placeholder', this.emptyText);
39238         }
39239         
39240         Roo.form.TextField.superclass.initEvents.call(this);
39241         if(this.validationEvent == 'keyup'){
39242             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39243             this.el.on('keyup', this.filterValidation, this);
39244         }
39245         else if(this.validationEvent !== false){
39246             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39247         }
39248         
39249         if(this.selectOnFocus){
39250             this.on("focus", this.preFocus, this);
39251             
39252         }
39253         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39254             this.el.on("keypress", this.filterKeys, this);
39255         }
39256         if(this.grow){
39257             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39258             this.el.on("click", this.autoSize,  this);
39259         }
39260         if(this.el.is('input[type=password]') && Roo.isSafari){
39261             this.el.on('keydown', this.SafariOnKeyDown, this);
39262         }
39263     },
39264
39265     processValue : function(value){
39266         if(this.stripCharsRe){
39267             var newValue = value.replace(this.stripCharsRe, '');
39268             if(newValue !== value){
39269                 this.setRawValue(newValue);
39270                 return newValue;
39271             }
39272         }
39273         return value;
39274     },
39275
39276     filterValidation : function(e){
39277         if(!e.isNavKeyPress()){
39278             this.validationTask.delay(this.validationDelay);
39279         }
39280     },
39281
39282     // private
39283     onKeyUp : function(e){
39284         if(!e.isNavKeyPress()){
39285             this.autoSize();
39286         }
39287     },
39288
39289     /**
39290      * Resets the current field value to the originally-loaded value and clears any validation messages.
39291      *  
39292      */
39293     reset : function(){
39294         Roo.form.TextField.superclass.reset.call(this);
39295        
39296     },
39297
39298     
39299     // private
39300     preFocus : function(){
39301         
39302         if(this.selectOnFocus){
39303             this.el.dom.select();
39304         }
39305     },
39306
39307     
39308     // private
39309     filterKeys : function(e){
39310         var k = e.getKey();
39311         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39312             return;
39313         }
39314         var c = e.getCharCode(), cc = String.fromCharCode(c);
39315         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39316             return;
39317         }
39318         if(!this.maskRe.test(cc)){
39319             e.stopEvent();
39320         }
39321     },
39322
39323     setValue : function(v){
39324         
39325         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39326         
39327         this.autoSize();
39328     },
39329
39330     /**
39331      * Validates a value according to the field's validation rules and marks the field as invalid
39332      * if the validation fails
39333      * @param {Mixed} value The value to validate
39334      * @return {Boolean} True if the value is valid, else false
39335      */
39336     validateValue : function(value){
39337         if(value.length < 1)  { // if it's blank
39338              if(this.allowBlank){
39339                 this.clearInvalid();
39340                 return true;
39341              }else{
39342                 this.markInvalid(this.blankText);
39343                 return false;
39344              }
39345         }
39346         if(value.length < this.minLength){
39347             this.markInvalid(String.format(this.minLengthText, this.minLength));
39348             return false;
39349         }
39350         if(value.length > this.maxLength){
39351             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39352             return false;
39353         }
39354         if(this.vtype){
39355             var vt = Roo.form.VTypes;
39356             if(!vt[this.vtype](value, this)){
39357                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39358                 return false;
39359             }
39360         }
39361         if(typeof this.validator == "function"){
39362             var msg = this.validator(value);
39363             if(msg !== true){
39364                 this.markInvalid(msg);
39365                 return false;
39366             }
39367         }
39368         if(this.regex && !this.regex.test(value)){
39369             this.markInvalid(this.regexText);
39370             return false;
39371         }
39372         return true;
39373     },
39374
39375     /**
39376      * Selects text in this field
39377      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39378      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39379      */
39380     selectText : function(start, end){
39381         var v = this.getRawValue();
39382         if(v.length > 0){
39383             start = start === undefined ? 0 : start;
39384             end = end === undefined ? v.length : end;
39385             var d = this.el.dom;
39386             if(d.setSelectionRange){
39387                 d.setSelectionRange(start, end);
39388             }else if(d.createTextRange){
39389                 var range = d.createTextRange();
39390                 range.moveStart("character", start);
39391                 range.moveEnd("character", v.length-end);
39392                 range.select();
39393             }
39394         }
39395     },
39396
39397     /**
39398      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39399      * This only takes effect if grow = true, and fires the autosize event.
39400      */
39401     autoSize : function(){
39402         if(!this.grow || !this.rendered){
39403             return;
39404         }
39405         if(!this.metrics){
39406             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39407         }
39408         var el = this.el;
39409         var v = el.dom.value;
39410         var d = document.createElement('div');
39411         d.appendChild(document.createTextNode(v));
39412         v = d.innerHTML;
39413         d = null;
39414         v += "&#160;";
39415         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39416         this.el.setWidth(w);
39417         this.fireEvent("autosize", this, w);
39418     },
39419     
39420     // private
39421     SafariOnKeyDown : function(event)
39422     {
39423         // this is a workaround for a password hang bug on chrome/ webkit.
39424         
39425         var isSelectAll = false;
39426         
39427         if(this.el.dom.selectionEnd > 0){
39428             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39429         }
39430         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39431             event.preventDefault();
39432             this.setValue('');
39433             return;
39434         }
39435         
39436         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39437             
39438             event.preventDefault();
39439             // this is very hacky as keydown always get's upper case.
39440             
39441             var cc = String.fromCharCode(event.getCharCode());
39442             
39443             
39444             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39445             
39446         }
39447         
39448         
39449     }
39450 });/*
39451  * Based on:
39452  * Ext JS Library 1.1.1
39453  * Copyright(c) 2006-2007, Ext JS, LLC.
39454  *
39455  * Originally Released Under LGPL - original licence link has changed is not relivant.
39456  *
39457  * Fork - LGPL
39458  * <script type="text/javascript">
39459  */
39460  
39461 /**
39462  * @class Roo.form.Hidden
39463  * @extends Roo.form.TextField
39464  * Simple Hidden element used on forms 
39465  * 
39466  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39467  * 
39468  * @constructor
39469  * Creates a new Hidden form element.
39470  * @param {Object} config Configuration options
39471  */
39472
39473
39474
39475 // easy hidden field...
39476 Roo.form.Hidden = function(config){
39477     Roo.form.Hidden.superclass.constructor.call(this, config);
39478 };
39479   
39480 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39481     fieldLabel:      '',
39482     inputType:      'hidden',
39483     width:          50,
39484     allowBlank:     true,
39485     labelSeparator: '',
39486     hidden:         true,
39487     itemCls :       'x-form-item-display-none'
39488
39489
39490 });
39491
39492
39493 /*
39494  * Based on:
39495  * Ext JS Library 1.1.1
39496  * Copyright(c) 2006-2007, Ext JS, LLC.
39497  *
39498  * Originally Released Under LGPL - original licence link has changed is not relivant.
39499  *
39500  * Fork - LGPL
39501  * <script type="text/javascript">
39502  */
39503  
39504 /**
39505  * @class Roo.form.TriggerField
39506  * @extends Roo.form.TextField
39507  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39508  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39509  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39510  * for which you can provide a custom implementation.  For example:
39511  * <pre><code>
39512 var trigger = new Roo.form.TriggerField();
39513 trigger.onTriggerClick = myTriggerFn;
39514 trigger.applyTo('my-field');
39515 </code></pre>
39516  *
39517  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39518  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39519  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39520  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39521  * @constructor
39522  * Create a new TriggerField.
39523  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39524  * to the base TextField)
39525  */
39526 Roo.form.TriggerField = function(config){
39527     this.mimicing = false;
39528     Roo.form.TriggerField.superclass.constructor.call(this, config);
39529 };
39530
39531 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39532     /**
39533      * @cfg {String} triggerClass A CSS class to apply to the trigger
39534      */
39535     /**
39536      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39537      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39538      */
39539     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39540     /**
39541      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39542      */
39543     hideTrigger:false,
39544
39545     /** @cfg {Boolean} grow @hide */
39546     /** @cfg {Number} growMin @hide */
39547     /** @cfg {Number} growMax @hide */
39548
39549     /**
39550      * @hide 
39551      * @method
39552      */
39553     autoSize: Roo.emptyFn,
39554     // private
39555     monitorTab : true,
39556     // private
39557     deferHeight : true,
39558
39559     
39560     actionMode : 'wrap',
39561     // private
39562     onResize : function(w, h){
39563         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39564         if(typeof w == 'number'){
39565             var x = w - this.trigger.getWidth();
39566             this.el.setWidth(this.adjustWidth('input', x));
39567             this.trigger.setStyle('left', x+'px');
39568         }
39569     },
39570
39571     // private
39572     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39573
39574     // private
39575     getResizeEl : function(){
39576         return this.wrap;
39577     },
39578
39579     // private
39580     getPositionEl : function(){
39581         return this.wrap;
39582     },
39583
39584     // private
39585     alignErrorIcon : function(){
39586         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39587     },
39588
39589     // private
39590     onRender : function(ct, position){
39591         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39592         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39593         this.trigger = this.wrap.createChild(this.triggerConfig ||
39594                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39595         if(this.hideTrigger){
39596             this.trigger.setDisplayed(false);
39597         }
39598         this.initTrigger();
39599         if(!this.width){
39600             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39601         }
39602     },
39603
39604     // private
39605     initTrigger : function(){
39606         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39607         this.trigger.addClassOnOver('x-form-trigger-over');
39608         this.trigger.addClassOnClick('x-form-trigger-click');
39609     },
39610
39611     // private
39612     onDestroy : function(){
39613         if(this.trigger){
39614             this.trigger.removeAllListeners();
39615             this.trigger.remove();
39616         }
39617         if(this.wrap){
39618             this.wrap.remove();
39619         }
39620         Roo.form.TriggerField.superclass.onDestroy.call(this);
39621     },
39622
39623     // private
39624     onFocus : function(){
39625         Roo.form.TriggerField.superclass.onFocus.call(this);
39626         if(!this.mimicing){
39627             this.wrap.addClass('x-trigger-wrap-focus');
39628             this.mimicing = true;
39629             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39630             if(this.monitorTab){
39631                 this.el.on("keydown", this.checkTab, this);
39632             }
39633         }
39634     },
39635
39636     // private
39637     checkTab : function(e){
39638         if(e.getKey() == e.TAB){
39639             this.triggerBlur();
39640         }
39641     },
39642
39643     // private
39644     onBlur : function(){
39645         // do nothing
39646     },
39647
39648     // private
39649     mimicBlur : function(e, t){
39650         if(!this.wrap.contains(t) && this.validateBlur()){
39651             this.triggerBlur();
39652         }
39653     },
39654
39655     // private
39656     triggerBlur : function(){
39657         this.mimicing = false;
39658         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39659         if(this.monitorTab){
39660             this.el.un("keydown", this.checkTab, this);
39661         }
39662         this.wrap.removeClass('x-trigger-wrap-focus');
39663         Roo.form.TriggerField.superclass.onBlur.call(this);
39664     },
39665
39666     // private
39667     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39668     validateBlur : function(e, t){
39669         return true;
39670     },
39671
39672     // private
39673     onDisable : function(){
39674         Roo.form.TriggerField.superclass.onDisable.call(this);
39675         if(this.wrap){
39676             this.wrap.addClass('x-item-disabled');
39677         }
39678     },
39679
39680     // private
39681     onEnable : function(){
39682         Roo.form.TriggerField.superclass.onEnable.call(this);
39683         if(this.wrap){
39684             this.wrap.removeClass('x-item-disabled');
39685         }
39686     },
39687
39688     // private
39689     onShow : function(){
39690         var ae = this.getActionEl();
39691         
39692         if(ae){
39693             ae.dom.style.display = '';
39694             ae.dom.style.visibility = 'visible';
39695         }
39696     },
39697
39698     // private
39699     
39700     onHide : function(){
39701         var ae = this.getActionEl();
39702         ae.dom.style.display = 'none';
39703     },
39704
39705     /**
39706      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39707      * by an implementing function.
39708      * @method
39709      * @param {EventObject} e
39710      */
39711     onTriggerClick : Roo.emptyFn
39712 });
39713
39714 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39715 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39716 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39717 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39718     initComponent : function(){
39719         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39720
39721         this.triggerConfig = {
39722             tag:'span', cls:'x-form-twin-triggers', cn:[
39723             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39724             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39725         ]};
39726     },
39727
39728     getTrigger : function(index){
39729         return this.triggers[index];
39730     },
39731
39732     initTrigger : function(){
39733         var ts = this.trigger.select('.x-form-trigger', true);
39734         this.wrap.setStyle('overflow', 'hidden');
39735         var triggerField = this;
39736         ts.each(function(t, all, index){
39737             t.hide = function(){
39738                 var w = triggerField.wrap.getWidth();
39739                 this.dom.style.display = 'none';
39740                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39741             };
39742             t.show = function(){
39743                 var w = triggerField.wrap.getWidth();
39744                 this.dom.style.display = '';
39745                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39746             };
39747             var triggerIndex = 'Trigger'+(index+1);
39748
39749             if(this['hide'+triggerIndex]){
39750                 t.dom.style.display = 'none';
39751             }
39752             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39753             t.addClassOnOver('x-form-trigger-over');
39754             t.addClassOnClick('x-form-trigger-click');
39755         }, this);
39756         this.triggers = ts.elements;
39757     },
39758
39759     onTrigger1Click : Roo.emptyFn,
39760     onTrigger2Click : Roo.emptyFn
39761 });/*
39762  * Based on:
39763  * Ext JS Library 1.1.1
39764  * Copyright(c) 2006-2007, Ext JS, LLC.
39765  *
39766  * Originally Released Under LGPL - original licence link has changed is not relivant.
39767  *
39768  * Fork - LGPL
39769  * <script type="text/javascript">
39770  */
39771  
39772 /**
39773  * @class Roo.form.TextArea
39774  * @extends Roo.form.TextField
39775  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39776  * support for auto-sizing.
39777  * @constructor
39778  * Creates a new TextArea
39779  * @param {Object} config Configuration options
39780  */
39781 Roo.form.TextArea = function(config){
39782     Roo.form.TextArea.superclass.constructor.call(this, config);
39783     // these are provided exchanges for backwards compat
39784     // minHeight/maxHeight were replaced by growMin/growMax to be
39785     // compatible with TextField growing config values
39786     if(this.minHeight !== undefined){
39787         this.growMin = this.minHeight;
39788     }
39789     if(this.maxHeight !== undefined){
39790         this.growMax = this.maxHeight;
39791     }
39792 };
39793
39794 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39795     /**
39796      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39797      */
39798     growMin : 60,
39799     /**
39800      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39801      */
39802     growMax: 1000,
39803     /**
39804      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39805      * in the field (equivalent to setting overflow: hidden, defaults to false)
39806      */
39807     preventScrollbars: false,
39808     /**
39809      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39810      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39811      */
39812
39813     // private
39814     onRender : function(ct, position){
39815         if(!this.el){
39816             this.defaultAutoCreate = {
39817                 tag: "textarea",
39818                 style:"width:300px;height:60px;",
39819                 autocomplete: "new-password"
39820             };
39821         }
39822         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39823         if(this.grow){
39824             this.textSizeEl = Roo.DomHelper.append(document.body, {
39825                 tag: "pre", cls: "x-form-grow-sizer"
39826             });
39827             if(this.preventScrollbars){
39828                 this.el.setStyle("overflow", "hidden");
39829             }
39830             this.el.setHeight(this.growMin);
39831         }
39832     },
39833
39834     onDestroy : function(){
39835         if(this.textSizeEl){
39836             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39837         }
39838         Roo.form.TextArea.superclass.onDestroy.call(this);
39839     },
39840
39841     // private
39842     onKeyUp : function(e){
39843         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39844             this.autoSize();
39845         }
39846     },
39847
39848     /**
39849      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39850      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39851      */
39852     autoSize : function(){
39853         if(!this.grow || !this.textSizeEl){
39854             return;
39855         }
39856         var el = this.el;
39857         var v = el.dom.value;
39858         var ts = this.textSizeEl;
39859
39860         ts.innerHTML = '';
39861         ts.appendChild(document.createTextNode(v));
39862         v = ts.innerHTML;
39863
39864         Roo.fly(ts).setWidth(this.el.getWidth());
39865         if(v.length < 1){
39866             v = "&#160;&#160;";
39867         }else{
39868             if(Roo.isIE){
39869                 v = v.replace(/\n/g, '<p>&#160;</p>');
39870             }
39871             v += "&#160;\n&#160;";
39872         }
39873         ts.innerHTML = v;
39874         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39875         if(h != this.lastHeight){
39876             this.lastHeight = h;
39877             this.el.setHeight(h);
39878             this.fireEvent("autosize", this, h);
39879         }
39880     }
39881 });/*
39882  * Based on:
39883  * Ext JS Library 1.1.1
39884  * Copyright(c) 2006-2007, Ext JS, LLC.
39885  *
39886  * Originally Released Under LGPL - original licence link has changed is not relivant.
39887  *
39888  * Fork - LGPL
39889  * <script type="text/javascript">
39890  */
39891  
39892
39893 /**
39894  * @class Roo.form.NumberField
39895  * @extends Roo.form.TextField
39896  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39897  * @constructor
39898  * Creates a new NumberField
39899  * @param {Object} config Configuration options
39900  */
39901 Roo.form.NumberField = function(config){
39902     Roo.form.NumberField.superclass.constructor.call(this, config);
39903 };
39904
39905 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39906     /**
39907      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39908      */
39909     fieldClass: "x-form-field x-form-num-field",
39910     /**
39911      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39912      */
39913     allowDecimals : true,
39914     /**
39915      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39916      */
39917     decimalSeparator : ".",
39918     /**
39919      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39920      */
39921     decimalPrecision : 2,
39922     /**
39923      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39924      */
39925     allowNegative : true,
39926     /**
39927      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39928      */
39929     minValue : Number.NEGATIVE_INFINITY,
39930     /**
39931      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39932      */
39933     maxValue : Number.MAX_VALUE,
39934     /**
39935      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39936      */
39937     minText : "The minimum value for this field is {0}",
39938     /**
39939      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39940      */
39941     maxText : "The maximum value for this field is {0}",
39942     /**
39943      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39944      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39945      */
39946     nanText : "{0} is not a valid number",
39947
39948     // private
39949     initEvents : function(){
39950         Roo.form.NumberField.superclass.initEvents.call(this);
39951         var allowed = "0123456789";
39952         if(this.allowDecimals){
39953             allowed += this.decimalSeparator;
39954         }
39955         if(this.allowNegative){
39956             allowed += "-";
39957         }
39958         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39959         var keyPress = function(e){
39960             var k = e.getKey();
39961             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39962                 return;
39963             }
39964             var c = e.getCharCode();
39965             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39966                 e.stopEvent();
39967             }
39968         };
39969         this.el.on("keypress", keyPress, this);
39970     },
39971
39972     // private
39973     validateValue : function(value){
39974         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
39975             return false;
39976         }
39977         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39978              return true;
39979         }
39980         var num = this.parseValue(value);
39981         if(isNaN(num)){
39982             this.markInvalid(String.format(this.nanText, value));
39983             return false;
39984         }
39985         if(num < this.minValue){
39986             this.markInvalid(String.format(this.minText, this.minValue));
39987             return false;
39988         }
39989         if(num > this.maxValue){
39990             this.markInvalid(String.format(this.maxText, this.maxValue));
39991             return false;
39992         }
39993         return true;
39994     },
39995
39996     getValue : function(){
39997         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
39998     },
39999
40000     // private
40001     parseValue : function(value){
40002         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40003         return isNaN(value) ? '' : value;
40004     },
40005
40006     // private
40007     fixPrecision : function(value){
40008         var nan = isNaN(value);
40009         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40010             return nan ? '' : value;
40011         }
40012         return parseFloat(value).toFixed(this.decimalPrecision);
40013     },
40014
40015     setValue : function(v){
40016         v = this.fixPrecision(v);
40017         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40018     },
40019
40020     // private
40021     decimalPrecisionFcn : function(v){
40022         return Math.floor(v);
40023     },
40024
40025     beforeBlur : function(){
40026         var v = this.parseValue(this.getRawValue());
40027         if(v){
40028             this.setValue(v);
40029         }
40030     }
40031 });/*
40032  * Based on:
40033  * Ext JS Library 1.1.1
40034  * Copyright(c) 2006-2007, Ext JS, LLC.
40035  *
40036  * Originally Released Under LGPL - original licence link has changed is not relivant.
40037  *
40038  * Fork - LGPL
40039  * <script type="text/javascript">
40040  */
40041  
40042 /**
40043  * @class Roo.form.DateField
40044  * @extends Roo.form.TriggerField
40045  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40046 * @constructor
40047 * Create a new DateField
40048 * @param {Object} config
40049  */
40050 Roo.form.DateField = function(config){
40051     Roo.form.DateField.superclass.constructor.call(this, config);
40052     
40053       this.addEvents({
40054          
40055         /**
40056          * @event select
40057          * Fires when a date is selected
40058              * @param {Roo.form.DateField} combo This combo box
40059              * @param {Date} date The date selected
40060              */
40061         'select' : true
40062          
40063     });
40064     
40065     
40066     if(typeof this.minValue == "string") {
40067         this.minValue = this.parseDate(this.minValue);
40068     }
40069     if(typeof this.maxValue == "string") {
40070         this.maxValue = this.parseDate(this.maxValue);
40071     }
40072     this.ddMatch = null;
40073     if(this.disabledDates){
40074         var dd = this.disabledDates;
40075         var re = "(?:";
40076         for(var i = 0; i < dd.length; i++){
40077             re += dd[i];
40078             if(i != dd.length-1) {
40079                 re += "|";
40080             }
40081         }
40082         this.ddMatch = new RegExp(re + ")");
40083     }
40084 };
40085
40086 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40087     /**
40088      * @cfg {String} format
40089      * The default date format string which can be overriden for localization support.  The format must be
40090      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40091      */
40092     format : "m/d/y",
40093     /**
40094      * @cfg {String} altFormats
40095      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40096      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40097      */
40098     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40099     /**
40100      * @cfg {Array} disabledDays
40101      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40102      */
40103     disabledDays : null,
40104     /**
40105      * @cfg {String} disabledDaysText
40106      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40107      */
40108     disabledDaysText : "Disabled",
40109     /**
40110      * @cfg {Array} disabledDates
40111      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40112      * expression so they are very powerful. Some examples:
40113      * <ul>
40114      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40115      * <li>["03/08", "09/16"] would disable those days for every year</li>
40116      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40117      * <li>["03/../2006"] would disable every day in March 2006</li>
40118      * <li>["^03"] would disable every day in every March</li>
40119      * </ul>
40120      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40121      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40122      */
40123     disabledDates : null,
40124     /**
40125      * @cfg {String} disabledDatesText
40126      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40127      */
40128     disabledDatesText : "Disabled",
40129     /**
40130      * @cfg {Date/String} minValue
40131      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40132      * valid format (defaults to null).
40133      */
40134     minValue : null,
40135     /**
40136      * @cfg {Date/String} maxValue
40137      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40138      * valid format (defaults to null).
40139      */
40140     maxValue : null,
40141     /**
40142      * @cfg {String} minText
40143      * The error text to display when the date in the cell is before minValue (defaults to
40144      * 'The date in this field must be after {minValue}').
40145      */
40146     minText : "The date in this field must be equal to or after {0}",
40147     /**
40148      * @cfg {String} maxText
40149      * The error text to display when the date in the cell is after maxValue (defaults to
40150      * 'The date in this field must be before {maxValue}').
40151      */
40152     maxText : "The date in this field must be equal to or before {0}",
40153     /**
40154      * @cfg {String} invalidText
40155      * The error text to display when the date in the field is invalid (defaults to
40156      * '{value} is not a valid date - it must be in the format {format}').
40157      */
40158     invalidText : "{0} is not a valid date - it must be in the format {1}",
40159     /**
40160      * @cfg {String} triggerClass
40161      * An additional CSS class used to style the trigger button.  The trigger will always get the
40162      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40163      * which displays a calendar icon).
40164      */
40165     triggerClass : 'x-form-date-trigger',
40166     
40167
40168     /**
40169      * @cfg {Boolean} useIso
40170      * if enabled, then the date field will use a hidden field to store the 
40171      * real value as iso formated date. default (false)
40172      */ 
40173     useIso : false,
40174     /**
40175      * @cfg {String/Object} autoCreate
40176      * A DomHelper element spec, or true for a default element spec (defaults to
40177      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40178      */ 
40179     // private
40180     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40181     
40182     // private
40183     hiddenField: false,
40184     
40185     onRender : function(ct, position)
40186     {
40187         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40188         if (this.useIso) {
40189             //this.el.dom.removeAttribute('name'); 
40190             Roo.log("Changing name?");
40191             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40192             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40193                     'before', true);
40194             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40195             // prevent input submission
40196             this.hiddenName = this.name;
40197         }
40198             
40199             
40200     },
40201     
40202     // private
40203     validateValue : function(value)
40204     {
40205         value = this.formatDate(value);
40206         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40207             Roo.log('super failed');
40208             return false;
40209         }
40210         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40211              return true;
40212         }
40213         var svalue = value;
40214         value = this.parseDate(value);
40215         if(!value){
40216             Roo.log('parse date failed' + svalue);
40217             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40218             return false;
40219         }
40220         var time = value.getTime();
40221         if(this.minValue && time < this.minValue.getTime()){
40222             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40223             return false;
40224         }
40225         if(this.maxValue && time > this.maxValue.getTime()){
40226             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40227             return false;
40228         }
40229         if(this.disabledDays){
40230             var day = value.getDay();
40231             for(var i = 0; i < this.disabledDays.length; i++) {
40232                 if(day === this.disabledDays[i]){
40233                     this.markInvalid(this.disabledDaysText);
40234                     return false;
40235                 }
40236             }
40237         }
40238         var fvalue = this.formatDate(value);
40239         if(this.ddMatch && this.ddMatch.test(fvalue)){
40240             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40241             return false;
40242         }
40243         return true;
40244     },
40245
40246     // private
40247     // Provides logic to override the default TriggerField.validateBlur which just returns true
40248     validateBlur : function(){
40249         return !this.menu || !this.menu.isVisible();
40250     },
40251     
40252     getName: function()
40253     {
40254         // returns hidden if it's set..
40255         if (!this.rendered) {return ''};
40256         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40257         
40258     },
40259
40260     /**
40261      * Returns the current date value of the date field.
40262      * @return {Date} The date value
40263      */
40264     getValue : function(){
40265         
40266         return  this.hiddenField ?
40267                 this.hiddenField.value :
40268                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40269     },
40270
40271     /**
40272      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40273      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40274      * (the default format used is "m/d/y").
40275      * <br />Usage:
40276      * <pre><code>
40277 //All of these calls set the same date value (May 4, 2006)
40278
40279 //Pass a date object:
40280 var dt = new Date('5/4/06');
40281 dateField.setValue(dt);
40282
40283 //Pass a date string (default format):
40284 dateField.setValue('5/4/06');
40285
40286 //Pass a date string (custom format):
40287 dateField.format = 'Y-m-d';
40288 dateField.setValue('2006-5-4');
40289 </code></pre>
40290      * @param {String/Date} date The date or valid date string
40291      */
40292     setValue : function(date){
40293         if (this.hiddenField) {
40294             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40295         }
40296         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40297         // make sure the value field is always stored as a date..
40298         this.value = this.parseDate(date);
40299         
40300         
40301     },
40302
40303     // private
40304     parseDate : function(value){
40305         if(!value || value instanceof Date){
40306             return value;
40307         }
40308         var v = Date.parseDate(value, this.format);
40309          if (!v && this.useIso) {
40310             v = Date.parseDate(value, 'Y-m-d');
40311         }
40312         if(!v && this.altFormats){
40313             if(!this.altFormatsArray){
40314                 this.altFormatsArray = this.altFormats.split("|");
40315             }
40316             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40317                 v = Date.parseDate(value, this.altFormatsArray[i]);
40318             }
40319         }
40320         return v;
40321     },
40322
40323     // private
40324     formatDate : function(date, fmt){
40325         return (!date || !(date instanceof Date)) ?
40326                date : date.dateFormat(fmt || this.format);
40327     },
40328
40329     // private
40330     menuListeners : {
40331         select: function(m, d){
40332             
40333             this.setValue(d);
40334             this.fireEvent('select', this, d);
40335         },
40336         show : function(){ // retain focus styling
40337             this.onFocus();
40338         },
40339         hide : function(){
40340             this.focus.defer(10, this);
40341             var ml = this.menuListeners;
40342             this.menu.un("select", ml.select,  this);
40343             this.menu.un("show", ml.show,  this);
40344             this.menu.un("hide", ml.hide,  this);
40345         }
40346     },
40347
40348     // private
40349     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40350     onTriggerClick : function(){
40351         if(this.disabled){
40352             return;
40353         }
40354         if(this.menu == null){
40355             this.menu = new Roo.menu.DateMenu();
40356         }
40357         Roo.apply(this.menu.picker,  {
40358             showClear: this.allowBlank,
40359             minDate : this.minValue,
40360             maxDate : this.maxValue,
40361             disabledDatesRE : this.ddMatch,
40362             disabledDatesText : this.disabledDatesText,
40363             disabledDays : this.disabledDays,
40364             disabledDaysText : this.disabledDaysText,
40365             format : this.useIso ? 'Y-m-d' : this.format,
40366             minText : String.format(this.minText, this.formatDate(this.minValue)),
40367             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40368         });
40369         this.menu.on(Roo.apply({}, this.menuListeners, {
40370             scope:this
40371         }));
40372         this.menu.picker.setValue(this.getValue() || new Date());
40373         this.menu.show(this.el, "tl-bl?");
40374     },
40375
40376     beforeBlur : function(){
40377         var v = this.parseDate(this.getRawValue());
40378         if(v){
40379             this.setValue(v);
40380         }
40381     },
40382
40383     /*@
40384      * overide
40385      * 
40386      */
40387     isDirty : function() {
40388         if(this.disabled) {
40389             return false;
40390         }
40391         
40392         if(typeof(this.startValue) === 'undefined'){
40393             return false;
40394         }
40395         
40396         return String(this.getValue()) !== String(this.startValue);
40397         
40398     }
40399 });/*
40400  * Based on:
40401  * Ext JS Library 1.1.1
40402  * Copyright(c) 2006-2007, Ext JS, LLC.
40403  *
40404  * Originally Released Under LGPL - original licence link has changed is not relivant.
40405  *
40406  * Fork - LGPL
40407  * <script type="text/javascript">
40408  */
40409  
40410 /**
40411  * @class Roo.form.MonthField
40412  * @extends Roo.form.TriggerField
40413  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40414 * @constructor
40415 * Create a new MonthField
40416 * @param {Object} config
40417  */
40418 Roo.form.MonthField = function(config){
40419     
40420     Roo.form.MonthField.superclass.constructor.call(this, config);
40421     
40422       this.addEvents({
40423          
40424         /**
40425          * @event select
40426          * Fires when a date is selected
40427              * @param {Roo.form.MonthFieeld} combo This combo box
40428              * @param {Date} date The date selected
40429              */
40430         'select' : true
40431          
40432     });
40433     
40434     
40435     if(typeof this.minValue == "string") {
40436         this.minValue = this.parseDate(this.minValue);
40437     }
40438     if(typeof this.maxValue == "string") {
40439         this.maxValue = this.parseDate(this.maxValue);
40440     }
40441     this.ddMatch = null;
40442     if(this.disabledDates){
40443         var dd = this.disabledDates;
40444         var re = "(?:";
40445         for(var i = 0; i < dd.length; i++){
40446             re += dd[i];
40447             if(i != dd.length-1) {
40448                 re += "|";
40449             }
40450         }
40451         this.ddMatch = new RegExp(re + ")");
40452     }
40453 };
40454
40455 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40456     /**
40457      * @cfg {String} format
40458      * The default date format string which can be overriden for localization support.  The format must be
40459      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40460      */
40461     format : "M Y",
40462     /**
40463      * @cfg {String} altFormats
40464      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40465      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40466      */
40467     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40468     /**
40469      * @cfg {Array} disabledDays
40470      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40471      */
40472     disabledDays : [0,1,2,3,4,5,6],
40473     /**
40474      * @cfg {String} disabledDaysText
40475      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40476      */
40477     disabledDaysText : "Disabled",
40478     /**
40479      * @cfg {Array} disabledDates
40480      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40481      * expression so they are very powerful. Some examples:
40482      * <ul>
40483      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40484      * <li>["03/08", "09/16"] would disable those days for every year</li>
40485      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40486      * <li>["03/../2006"] would disable every day in March 2006</li>
40487      * <li>["^03"] would disable every day in every March</li>
40488      * </ul>
40489      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40490      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40491      */
40492     disabledDates : null,
40493     /**
40494      * @cfg {String} disabledDatesText
40495      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40496      */
40497     disabledDatesText : "Disabled",
40498     /**
40499      * @cfg {Date/String} minValue
40500      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40501      * valid format (defaults to null).
40502      */
40503     minValue : null,
40504     /**
40505      * @cfg {Date/String} maxValue
40506      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40507      * valid format (defaults to null).
40508      */
40509     maxValue : null,
40510     /**
40511      * @cfg {String} minText
40512      * The error text to display when the date in the cell is before minValue (defaults to
40513      * 'The date in this field must be after {minValue}').
40514      */
40515     minText : "The date in this field must be equal to or after {0}",
40516     /**
40517      * @cfg {String} maxTextf
40518      * The error text to display when the date in the cell is after maxValue (defaults to
40519      * 'The date in this field must be before {maxValue}').
40520      */
40521     maxText : "The date in this field must be equal to or before {0}",
40522     /**
40523      * @cfg {String} invalidText
40524      * The error text to display when the date in the field is invalid (defaults to
40525      * '{value} is not a valid date - it must be in the format {format}').
40526      */
40527     invalidText : "{0} is not a valid date - it must be in the format {1}",
40528     /**
40529      * @cfg {String} triggerClass
40530      * An additional CSS class used to style the trigger button.  The trigger will always get the
40531      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40532      * which displays a calendar icon).
40533      */
40534     triggerClass : 'x-form-date-trigger',
40535     
40536
40537     /**
40538      * @cfg {Boolean} useIso
40539      * if enabled, then the date field will use a hidden field to store the 
40540      * real value as iso formated date. default (true)
40541      */ 
40542     useIso : true,
40543     /**
40544      * @cfg {String/Object} autoCreate
40545      * A DomHelper element spec, or true for a default element spec (defaults to
40546      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40547      */ 
40548     // private
40549     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40550     
40551     // private
40552     hiddenField: false,
40553     
40554     hideMonthPicker : false,
40555     
40556     onRender : function(ct, position)
40557     {
40558         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40559         if (this.useIso) {
40560             this.el.dom.removeAttribute('name'); 
40561             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40562                     'before', true);
40563             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40564             // prevent input submission
40565             this.hiddenName = this.name;
40566         }
40567             
40568             
40569     },
40570     
40571     // private
40572     validateValue : function(value)
40573     {
40574         value = this.formatDate(value);
40575         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40576             return false;
40577         }
40578         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40579              return true;
40580         }
40581         var svalue = value;
40582         value = this.parseDate(value);
40583         if(!value){
40584             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40585             return false;
40586         }
40587         var time = value.getTime();
40588         if(this.minValue && time < this.minValue.getTime()){
40589             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40590             return false;
40591         }
40592         if(this.maxValue && time > this.maxValue.getTime()){
40593             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40594             return false;
40595         }
40596         /*if(this.disabledDays){
40597             var day = value.getDay();
40598             for(var i = 0; i < this.disabledDays.length; i++) {
40599                 if(day === this.disabledDays[i]){
40600                     this.markInvalid(this.disabledDaysText);
40601                     return false;
40602                 }
40603             }
40604         }
40605         */
40606         var fvalue = this.formatDate(value);
40607         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40608             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40609             return false;
40610         }
40611         */
40612         return true;
40613     },
40614
40615     // private
40616     // Provides logic to override the default TriggerField.validateBlur which just returns true
40617     validateBlur : function(){
40618         return !this.menu || !this.menu.isVisible();
40619     },
40620
40621     /**
40622      * Returns the current date value of the date field.
40623      * @return {Date} The date value
40624      */
40625     getValue : function(){
40626         
40627         
40628         
40629         return  this.hiddenField ?
40630                 this.hiddenField.value :
40631                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40632     },
40633
40634     /**
40635      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40636      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40637      * (the default format used is "m/d/y").
40638      * <br />Usage:
40639      * <pre><code>
40640 //All of these calls set the same date value (May 4, 2006)
40641
40642 //Pass a date object:
40643 var dt = new Date('5/4/06');
40644 monthField.setValue(dt);
40645
40646 //Pass a date string (default format):
40647 monthField.setValue('5/4/06');
40648
40649 //Pass a date string (custom format):
40650 monthField.format = 'Y-m-d';
40651 monthField.setValue('2006-5-4');
40652 </code></pre>
40653      * @param {String/Date} date The date or valid date string
40654      */
40655     setValue : function(date){
40656         Roo.log('month setValue' + date);
40657         // can only be first of month..
40658         
40659         var val = this.parseDate(date);
40660         
40661         if (this.hiddenField) {
40662             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40663         }
40664         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40665         this.value = this.parseDate(date);
40666     },
40667
40668     // private
40669     parseDate : function(value){
40670         if(!value || value instanceof Date){
40671             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40672             return value;
40673         }
40674         var v = Date.parseDate(value, this.format);
40675         if (!v && this.useIso) {
40676             v = Date.parseDate(value, 'Y-m-d');
40677         }
40678         if (v) {
40679             // 
40680             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40681         }
40682         
40683         
40684         if(!v && this.altFormats){
40685             if(!this.altFormatsArray){
40686                 this.altFormatsArray = this.altFormats.split("|");
40687             }
40688             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40689                 v = Date.parseDate(value, this.altFormatsArray[i]);
40690             }
40691         }
40692         return v;
40693     },
40694
40695     // private
40696     formatDate : function(date, fmt){
40697         return (!date || !(date instanceof Date)) ?
40698                date : date.dateFormat(fmt || this.format);
40699     },
40700
40701     // private
40702     menuListeners : {
40703         select: function(m, d){
40704             this.setValue(d);
40705             this.fireEvent('select', this, d);
40706         },
40707         show : function(){ // retain focus styling
40708             this.onFocus();
40709         },
40710         hide : function(){
40711             this.focus.defer(10, this);
40712             var ml = this.menuListeners;
40713             this.menu.un("select", ml.select,  this);
40714             this.menu.un("show", ml.show,  this);
40715             this.menu.un("hide", ml.hide,  this);
40716         }
40717     },
40718     // private
40719     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40720     onTriggerClick : function(){
40721         if(this.disabled){
40722             return;
40723         }
40724         if(this.menu == null){
40725             this.menu = new Roo.menu.DateMenu();
40726            
40727         }
40728         
40729         Roo.apply(this.menu.picker,  {
40730             
40731             showClear: this.allowBlank,
40732             minDate : this.minValue,
40733             maxDate : this.maxValue,
40734             disabledDatesRE : this.ddMatch,
40735             disabledDatesText : this.disabledDatesText,
40736             
40737             format : this.useIso ? 'Y-m-d' : this.format,
40738             minText : String.format(this.minText, this.formatDate(this.minValue)),
40739             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40740             
40741         });
40742          this.menu.on(Roo.apply({}, this.menuListeners, {
40743             scope:this
40744         }));
40745        
40746         
40747         var m = this.menu;
40748         var p = m.picker;
40749         
40750         // hide month picker get's called when we called by 'before hide';
40751         
40752         var ignorehide = true;
40753         p.hideMonthPicker  = function(disableAnim){
40754             if (ignorehide) {
40755                 return;
40756             }
40757              if(this.monthPicker){
40758                 Roo.log("hideMonthPicker called");
40759                 if(disableAnim === true){
40760                     this.monthPicker.hide();
40761                 }else{
40762                     this.monthPicker.slideOut('t', {duration:.2});
40763                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40764                     p.fireEvent("select", this, this.value);
40765                     m.hide();
40766                 }
40767             }
40768         }
40769         
40770         Roo.log('picker set value');
40771         Roo.log(this.getValue());
40772         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40773         m.show(this.el, 'tl-bl?');
40774         ignorehide  = false;
40775         // this will trigger hideMonthPicker..
40776         
40777         
40778         // hidden the day picker
40779         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40780         
40781         
40782         
40783       
40784         
40785         p.showMonthPicker.defer(100, p);
40786     
40787         
40788        
40789     },
40790
40791     beforeBlur : function(){
40792         var v = this.parseDate(this.getRawValue());
40793         if(v){
40794             this.setValue(v);
40795         }
40796     }
40797
40798     /** @cfg {Boolean} grow @hide */
40799     /** @cfg {Number} growMin @hide */
40800     /** @cfg {Number} growMax @hide */
40801     /**
40802      * @hide
40803      * @method autoSize
40804      */
40805 });/*
40806  * Based on:
40807  * Ext JS Library 1.1.1
40808  * Copyright(c) 2006-2007, Ext JS, LLC.
40809  *
40810  * Originally Released Under LGPL - original licence link has changed is not relivant.
40811  *
40812  * Fork - LGPL
40813  * <script type="text/javascript">
40814  */
40815  
40816
40817 /**
40818  * @class Roo.form.ComboBox
40819  * @extends Roo.form.TriggerField
40820  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40821  * @constructor
40822  * Create a new ComboBox.
40823  * @param {Object} config Configuration options
40824  */
40825 Roo.form.ComboBox = function(config){
40826     Roo.form.ComboBox.superclass.constructor.call(this, config);
40827     this.addEvents({
40828         /**
40829          * @event expand
40830          * Fires when the dropdown list is expanded
40831              * @param {Roo.form.ComboBox} combo This combo box
40832              */
40833         'expand' : true,
40834         /**
40835          * @event collapse
40836          * Fires when the dropdown list is collapsed
40837              * @param {Roo.form.ComboBox} combo This combo box
40838              */
40839         'collapse' : true,
40840         /**
40841          * @event beforeselect
40842          * Fires before a list item is selected. Return false to cancel the selection.
40843              * @param {Roo.form.ComboBox} combo This combo box
40844              * @param {Roo.data.Record} record The data record returned from the underlying store
40845              * @param {Number} index The index of the selected item in the dropdown list
40846              */
40847         'beforeselect' : true,
40848         /**
40849          * @event select
40850          * Fires when a list item is selected
40851              * @param {Roo.form.ComboBox} combo This combo box
40852              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40853              * @param {Number} index The index of the selected item in the dropdown list
40854              */
40855         'select' : true,
40856         /**
40857          * @event beforequery
40858          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40859          * The event object passed has these properties:
40860              * @param {Roo.form.ComboBox} combo This combo box
40861              * @param {String} query The query
40862              * @param {Boolean} forceAll true to force "all" query
40863              * @param {Boolean} cancel true to cancel the query
40864              * @param {Object} e The query event object
40865              */
40866         'beforequery': true,
40867          /**
40868          * @event add
40869          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40870              * @param {Roo.form.ComboBox} combo This combo box
40871              */
40872         'add' : true,
40873         /**
40874          * @event edit
40875          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40876              * @param {Roo.form.ComboBox} combo This combo box
40877              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40878              */
40879         'edit' : true
40880         
40881         
40882     });
40883     if(this.transform){
40884         this.allowDomMove = false;
40885         var s = Roo.getDom(this.transform);
40886         if(!this.hiddenName){
40887             this.hiddenName = s.name;
40888         }
40889         if(!this.store){
40890             this.mode = 'local';
40891             var d = [], opts = s.options;
40892             for(var i = 0, len = opts.length;i < len; i++){
40893                 var o = opts[i];
40894                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40895                 if(o.selected) {
40896                     this.value = value;
40897                 }
40898                 d.push([value, o.text]);
40899             }
40900             this.store = new Roo.data.SimpleStore({
40901                 'id': 0,
40902                 fields: ['value', 'text'],
40903                 data : d
40904             });
40905             this.valueField = 'value';
40906             this.displayField = 'text';
40907         }
40908         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40909         if(!this.lazyRender){
40910             this.target = true;
40911             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40912             s.parentNode.removeChild(s); // remove it
40913             this.render(this.el.parentNode);
40914         }else{
40915             s.parentNode.removeChild(s); // remove it
40916         }
40917
40918     }
40919     if (this.store) {
40920         this.store = Roo.factory(this.store, Roo.data);
40921     }
40922     
40923     this.selectedIndex = -1;
40924     if(this.mode == 'local'){
40925         if(config.queryDelay === undefined){
40926             this.queryDelay = 10;
40927         }
40928         if(config.minChars === undefined){
40929             this.minChars = 0;
40930         }
40931     }
40932 };
40933
40934 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40935     /**
40936      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40937      */
40938     /**
40939      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40940      * rendering into an Roo.Editor, defaults to false)
40941      */
40942     /**
40943      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40944      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40945      */
40946     /**
40947      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40948      */
40949     /**
40950      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40951      * the dropdown list (defaults to undefined, with no header element)
40952      */
40953
40954      /**
40955      * @cfg {String/Roo.Template} tpl The template to use to render the output
40956      */
40957      
40958     // private
40959     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40960     /**
40961      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40962      */
40963     listWidth: undefined,
40964     /**
40965      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40966      * mode = 'remote' or 'text' if mode = 'local')
40967      */
40968     displayField: undefined,
40969     /**
40970      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
40971      * mode = 'remote' or 'value' if mode = 'local'). 
40972      * Note: use of a valueField requires the user make a selection
40973      * in order for a value to be mapped.
40974      */
40975     valueField: undefined,
40976     
40977     
40978     /**
40979      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
40980      * field's data value (defaults to the underlying DOM element's name)
40981      */
40982     hiddenName: undefined,
40983     /**
40984      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
40985      */
40986     listClass: '',
40987     /**
40988      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
40989      */
40990     selectedClass: 'x-combo-selected',
40991     /**
40992      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40993      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
40994      * which displays a downward arrow icon).
40995      */
40996     triggerClass : 'x-form-arrow-trigger',
40997     /**
40998      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
40999      */
41000     shadow:'sides',
41001     /**
41002      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41003      * anchor positions (defaults to 'tl-bl')
41004      */
41005     listAlign: 'tl-bl?',
41006     /**
41007      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41008      */
41009     maxHeight: 300,
41010     /**
41011      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41012      * query specified by the allQuery config option (defaults to 'query')
41013      */
41014     triggerAction: 'query',
41015     /**
41016      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41017      * (defaults to 4, does not apply if editable = false)
41018      */
41019     minChars : 4,
41020     /**
41021      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41022      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41023      */
41024     typeAhead: false,
41025     /**
41026      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41027      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41028      */
41029     queryDelay: 500,
41030     /**
41031      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41032      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41033      */
41034     pageSize: 0,
41035     /**
41036      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41037      * when editable = true (defaults to false)
41038      */
41039     selectOnFocus:false,
41040     /**
41041      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41042      */
41043     queryParam: 'query',
41044     /**
41045      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41046      * when mode = 'remote' (defaults to 'Loading...')
41047      */
41048     loadingText: 'Loading...',
41049     /**
41050      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41051      */
41052     resizable: false,
41053     /**
41054      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41055      */
41056     handleHeight : 8,
41057     /**
41058      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41059      * traditional select (defaults to true)
41060      */
41061     editable: true,
41062     /**
41063      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41064      */
41065     allQuery: '',
41066     /**
41067      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41068      */
41069     mode: 'remote',
41070     /**
41071      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41072      * listWidth has a higher value)
41073      */
41074     minListWidth : 70,
41075     /**
41076      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41077      * allow the user to set arbitrary text into the field (defaults to false)
41078      */
41079     forceSelection:false,
41080     /**
41081      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41082      * if typeAhead = true (defaults to 250)
41083      */
41084     typeAheadDelay : 250,
41085     /**
41086      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41087      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41088      */
41089     valueNotFoundText : undefined,
41090     /**
41091      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41092      */
41093     blockFocus : false,
41094     
41095     /**
41096      * @cfg {Boolean} disableClear Disable showing of clear button.
41097      */
41098     disableClear : false,
41099     /**
41100      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41101      */
41102     alwaysQuery : false,
41103     
41104     //private
41105     addicon : false,
41106     editicon: false,
41107     
41108     // element that contains real text value.. (when hidden is used..)
41109      
41110     // private
41111     onRender : function(ct, position){
41112         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41113         if(this.hiddenName){
41114             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41115                     'before', true);
41116             this.hiddenField.value =
41117                 this.hiddenValue !== undefined ? this.hiddenValue :
41118                 this.value !== undefined ? this.value : '';
41119
41120             // prevent input submission
41121             this.el.dom.removeAttribute('name');
41122              
41123              
41124         }
41125         if(Roo.isGecko){
41126             this.el.dom.setAttribute('autocomplete', 'off');
41127         }
41128
41129         var cls = 'x-combo-list';
41130
41131         this.list = new Roo.Layer({
41132             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41133         });
41134
41135         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41136         this.list.setWidth(lw);
41137         this.list.swallowEvent('mousewheel');
41138         this.assetHeight = 0;
41139
41140         if(this.title){
41141             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41142             this.assetHeight += this.header.getHeight();
41143         }
41144
41145         this.innerList = this.list.createChild({cls:cls+'-inner'});
41146         this.innerList.on('mouseover', this.onViewOver, this);
41147         this.innerList.on('mousemove', this.onViewMove, this);
41148         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41149         
41150         if(this.allowBlank && !this.pageSize && !this.disableClear){
41151             this.footer = this.list.createChild({cls:cls+'-ft'});
41152             this.pageTb = new Roo.Toolbar(this.footer);
41153            
41154         }
41155         if(this.pageSize){
41156             this.footer = this.list.createChild({cls:cls+'-ft'});
41157             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41158                     {pageSize: this.pageSize});
41159             
41160         }
41161         
41162         if (this.pageTb && this.allowBlank && !this.disableClear) {
41163             var _this = this;
41164             this.pageTb.add(new Roo.Toolbar.Fill(), {
41165                 cls: 'x-btn-icon x-btn-clear',
41166                 text: '&#160;',
41167                 handler: function()
41168                 {
41169                     _this.collapse();
41170                     _this.clearValue();
41171                     _this.onSelect(false, -1);
41172                 }
41173             });
41174         }
41175         if (this.footer) {
41176             this.assetHeight += this.footer.getHeight();
41177         }
41178         
41179
41180         if(!this.tpl){
41181             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41182         }
41183
41184         this.view = new Roo.View(this.innerList, this.tpl, {
41185             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41186         });
41187
41188         this.view.on('click', this.onViewClick, this);
41189
41190         this.store.on('beforeload', this.onBeforeLoad, this);
41191         this.store.on('load', this.onLoad, this);
41192         this.store.on('loadexception', this.onLoadException, this);
41193
41194         if(this.resizable){
41195             this.resizer = new Roo.Resizable(this.list,  {
41196                pinned:true, handles:'se'
41197             });
41198             this.resizer.on('resize', function(r, w, h){
41199                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41200                 this.listWidth = w;
41201                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41202                 this.restrictHeight();
41203             }, this);
41204             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41205         }
41206         if(!this.editable){
41207             this.editable = true;
41208             this.setEditable(false);
41209         }  
41210         
41211         
41212         if (typeof(this.events.add.listeners) != 'undefined') {
41213             
41214             this.addicon = this.wrap.createChild(
41215                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41216        
41217             this.addicon.on('click', function(e) {
41218                 this.fireEvent('add', this);
41219             }, this);
41220         }
41221         if (typeof(this.events.edit.listeners) != 'undefined') {
41222             
41223             this.editicon = this.wrap.createChild(
41224                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41225             if (this.addicon) {
41226                 this.editicon.setStyle('margin-left', '40px');
41227             }
41228             this.editicon.on('click', function(e) {
41229                 
41230                 // we fire even  if inothing is selected..
41231                 this.fireEvent('edit', this, this.lastData );
41232                 
41233             }, this);
41234         }
41235         
41236         
41237         
41238     },
41239
41240     // private
41241     initEvents : function(){
41242         Roo.form.ComboBox.superclass.initEvents.call(this);
41243
41244         this.keyNav = new Roo.KeyNav(this.el, {
41245             "up" : function(e){
41246                 this.inKeyMode = true;
41247                 this.selectPrev();
41248             },
41249
41250             "down" : function(e){
41251                 if(!this.isExpanded()){
41252                     this.onTriggerClick();
41253                 }else{
41254                     this.inKeyMode = true;
41255                     this.selectNext();
41256                 }
41257             },
41258
41259             "enter" : function(e){
41260                 this.onViewClick();
41261                 //return true;
41262             },
41263
41264             "esc" : function(e){
41265                 this.collapse();
41266             },
41267
41268             "tab" : function(e){
41269                 this.onViewClick(false);
41270                 this.fireEvent("specialkey", this, e);
41271                 return true;
41272             },
41273
41274             scope : this,
41275
41276             doRelay : function(foo, bar, hname){
41277                 if(hname == 'down' || this.scope.isExpanded()){
41278                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41279                 }
41280                 return true;
41281             },
41282
41283             forceKeyDown: true
41284         });
41285         this.queryDelay = Math.max(this.queryDelay || 10,
41286                 this.mode == 'local' ? 10 : 250);
41287         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41288         if(this.typeAhead){
41289             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41290         }
41291         if(this.editable !== false){
41292             this.el.on("keyup", this.onKeyUp, this);
41293         }
41294         if(this.forceSelection){
41295             this.on('blur', this.doForce, this);
41296         }
41297     },
41298
41299     onDestroy : function(){
41300         if(this.view){
41301             this.view.setStore(null);
41302             this.view.el.removeAllListeners();
41303             this.view.el.remove();
41304             this.view.purgeListeners();
41305         }
41306         if(this.list){
41307             this.list.destroy();
41308         }
41309         if(this.store){
41310             this.store.un('beforeload', this.onBeforeLoad, this);
41311             this.store.un('load', this.onLoad, this);
41312             this.store.un('loadexception', this.onLoadException, this);
41313         }
41314         Roo.form.ComboBox.superclass.onDestroy.call(this);
41315     },
41316
41317     // private
41318     fireKey : function(e){
41319         if(e.isNavKeyPress() && !this.list.isVisible()){
41320             this.fireEvent("specialkey", this, e);
41321         }
41322     },
41323
41324     // private
41325     onResize: function(w, h){
41326         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41327         
41328         if(typeof w != 'number'){
41329             // we do not handle it!?!?
41330             return;
41331         }
41332         var tw = this.trigger.getWidth();
41333         tw += this.addicon ? this.addicon.getWidth() : 0;
41334         tw += this.editicon ? this.editicon.getWidth() : 0;
41335         var x = w - tw;
41336         this.el.setWidth( this.adjustWidth('input', x));
41337             
41338         this.trigger.setStyle('left', x+'px');
41339         
41340         if(this.list && this.listWidth === undefined){
41341             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41342             this.list.setWidth(lw);
41343             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41344         }
41345         
41346     
41347         
41348     },
41349
41350     /**
41351      * Allow or prevent the user from directly editing the field text.  If false is passed,
41352      * the user will only be able to select from the items defined in the dropdown list.  This method
41353      * is the runtime equivalent of setting the 'editable' config option at config time.
41354      * @param {Boolean} value True to allow the user to directly edit the field text
41355      */
41356     setEditable : function(value){
41357         if(value == this.editable){
41358             return;
41359         }
41360         this.editable = value;
41361         if(!value){
41362             this.el.dom.setAttribute('readOnly', true);
41363             this.el.on('mousedown', this.onTriggerClick,  this);
41364             this.el.addClass('x-combo-noedit');
41365         }else{
41366             this.el.dom.setAttribute('readOnly', false);
41367             this.el.un('mousedown', this.onTriggerClick,  this);
41368             this.el.removeClass('x-combo-noedit');
41369         }
41370     },
41371
41372     // private
41373     onBeforeLoad : function(){
41374         if(!this.hasFocus){
41375             return;
41376         }
41377         this.innerList.update(this.loadingText ?
41378                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41379         this.restrictHeight();
41380         this.selectedIndex = -1;
41381     },
41382
41383     // private
41384     onLoad : function(){
41385         if(!this.hasFocus){
41386             return;
41387         }
41388         if(this.store.getCount() > 0){
41389             this.expand();
41390             this.restrictHeight();
41391             if(this.lastQuery == this.allQuery){
41392                 if(this.editable){
41393                     this.el.dom.select();
41394                 }
41395                 if(!this.selectByValue(this.value, true)){
41396                     this.select(0, true);
41397                 }
41398             }else{
41399                 this.selectNext();
41400                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41401                     this.taTask.delay(this.typeAheadDelay);
41402                 }
41403             }
41404         }else{
41405             this.onEmptyResults();
41406         }
41407         //this.el.focus();
41408     },
41409     // private
41410     onLoadException : function()
41411     {
41412         this.collapse();
41413         Roo.log(this.store.reader.jsonData);
41414         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41415             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41416         }
41417         
41418         
41419     },
41420     // private
41421     onTypeAhead : function(){
41422         if(this.store.getCount() > 0){
41423             var r = this.store.getAt(0);
41424             var newValue = r.data[this.displayField];
41425             var len = newValue.length;
41426             var selStart = this.getRawValue().length;
41427             if(selStart != len){
41428                 this.setRawValue(newValue);
41429                 this.selectText(selStart, newValue.length);
41430             }
41431         }
41432     },
41433
41434     // private
41435     onSelect : function(record, index){
41436         if(this.fireEvent('beforeselect', this, record, index) !== false){
41437             this.setFromData(index > -1 ? record.data : false);
41438             this.collapse();
41439             this.fireEvent('select', this, record, index);
41440         }
41441     },
41442
41443     /**
41444      * Returns the currently selected field value or empty string if no value is set.
41445      * @return {String} value The selected value
41446      */
41447     getValue : function(){
41448         if(this.valueField){
41449             return typeof this.value != 'undefined' ? this.value : '';
41450         }
41451         return Roo.form.ComboBox.superclass.getValue.call(this);
41452     },
41453
41454     /**
41455      * Clears any text/value currently set in the field
41456      */
41457     clearValue : function(){
41458         if(this.hiddenField){
41459             this.hiddenField.value = '';
41460         }
41461         this.value = '';
41462         this.setRawValue('');
41463         this.lastSelectionText = '';
41464         
41465     },
41466
41467     /**
41468      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41469      * will be displayed in the field.  If the value does not match the data value of an existing item,
41470      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41471      * Otherwise the field will be blank (although the value will still be set).
41472      * @param {String} value The value to match
41473      */
41474     setValue : function(v){
41475         var text = v;
41476         if(this.valueField){
41477             var r = this.findRecord(this.valueField, v);
41478             if(r){
41479                 text = r.data[this.displayField];
41480             }else if(this.valueNotFoundText !== undefined){
41481                 text = this.valueNotFoundText;
41482             }
41483         }
41484         this.lastSelectionText = text;
41485         if(this.hiddenField){
41486             this.hiddenField.value = v;
41487         }
41488         Roo.form.ComboBox.superclass.setValue.call(this, text);
41489         this.value = v;
41490     },
41491     /**
41492      * @property {Object} the last set data for the element
41493      */
41494     
41495     lastData : false,
41496     /**
41497      * Sets the value of the field based on a object which is related to the record format for the store.
41498      * @param {Object} value the value to set as. or false on reset?
41499      */
41500     setFromData : function(o){
41501         var dv = ''; // display value
41502         var vv = ''; // value value..
41503         this.lastData = o;
41504         if (this.displayField) {
41505             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41506         } else {
41507             // this is an error condition!!!
41508             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41509         }
41510         
41511         if(this.valueField){
41512             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41513         }
41514         if(this.hiddenField){
41515             this.hiddenField.value = vv;
41516             
41517             this.lastSelectionText = dv;
41518             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41519             this.value = vv;
41520             return;
41521         }
41522         // no hidden field.. - we store the value in 'value', but still display
41523         // display field!!!!
41524         this.lastSelectionText = dv;
41525         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41526         this.value = vv;
41527         
41528         
41529     },
41530     // private
41531     reset : function(){
41532         // overridden so that last data is reset..
41533         this.setValue(this.resetValue);
41534         this.clearInvalid();
41535         this.lastData = false;
41536         if (this.view) {
41537             this.view.clearSelections();
41538         }
41539     },
41540     // private
41541     findRecord : function(prop, value){
41542         var record;
41543         if(this.store.getCount() > 0){
41544             this.store.each(function(r){
41545                 if(r.data[prop] == value){
41546                     record = r;
41547                     return false;
41548                 }
41549                 return true;
41550             });
41551         }
41552         return record;
41553     },
41554     
41555     getName: function()
41556     {
41557         // returns hidden if it's set..
41558         if (!this.rendered) {return ''};
41559         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41560         
41561     },
41562     // private
41563     onViewMove : function(e, t){
41564         this.inKeyMode = false;
41565     },
41566
41567     // private
41568     onViewOver : function(e, t){
41569         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41570             return;
41571         }
41572         var item = this.view.findItemFromChild(t);
41573         if(item){
41574             var index = this.view.indexOf(item);
41575             this.select(index, false);
41576         }
41577     },
41578
41579     // private
41580     onViewClick : function(doFocus)
41581     {
41582         var index = this.view.getSelectedIndexes()[0];
41583         var r = this.store.getAt(index);
41584         if(r){
41585             this.onSelect(r, index);
41586         }
41587         if(doFocus !== false && !this.blockFocus){
41588             this.el.focus();
41589         }
41590     },
41591
41592     // private
41593     restrictHeight : function(){
41594         this.innerList.dom.style.height = '';
41595         var inner = this.innerList.dom;
41596         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41597         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41598         this.list.beginUpdate();
41599         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41600         this.list.alignTo(this.el, this.listAlign);
41601         this.list.endUpdate();
41602     },
41603
41604     // private
41605     onEmptyResults : function(){
41606         this.collapse();
41607     },
41608
41609     /**
41610      * Returns true if the dropdown list is expanded, else false.
41611      */
41612     isExpanded : function(){
41613         return this.list.isVisible();
41614     },
41615
41616     /**
41617      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41618      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41619      * @param {String} value The data value of the item to select
41620      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41621      * selected item if it is not currently in view (defaults to true)
41622      * @return {Boolean} True if the value matched an item in the list, else false
41623      */
41624     selectByValue : function(v, scrollIntoView){
41625         if(v !== undefined && v !== null){
41626             var r = this.findRecord(this.valueField || this.displayField, v);
41627             if(r){
41628                 this.select(this.store.indexOf(r), scrollIntoView);
41629                 return true;
41630             }
41631         }
41632         return false;
41633     },
41634
41635     /**
41636      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41637      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41638      * @param {Number} index The zero-based index of the list item to select
41639      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41640      * selected item if it is not currently in view (defaults to true)
41641      */
41642     select : function(index, scrollIntoView){
41643         this.selectedIndex = index;
41644         this.view.select(index);
41645         if(scrollIntoView !== false){
41646             var el = this.view.getNode(index);
41647             if(el){
41648                 this.innerList.scrollChildIntoView(el, false);
41649             }
41650         }
41651     },
41652
41653     // private
41654     selectNext : function(){
41655         var ct = this.store.getCount();
41656         if(ct > 0){
41657             if(this.selectedIndex == -1){
41658                 this.select(0);
41659             }else if(this.selectedIndex < ct-1){
41660                 this.select(this.selectedIndex+1);
41661             }
41662         }
41663     },
41664
41665     // private
41666     selectPrev : function(){
41667         var ct = this.store.getCount();
41668         if(ct > 0){
41669             if(this.selectedIndex == -1){
41670                 this.select(0);
41671             }else if(this.selectedIndex != 0){
41672                 this.select(this.selectedIndex-1);
41673             }
41674         }
41675     },
41676
41677     // private
41678     onKeyUp : function(e){
41679         if(this.editable !== false && !e.isSpecialKey()){
41680             this.lastKey = e.getKey();
41681             this.dqTask.delay(this.queryDelay);
41682         }
41683     },
41684
41685     // private
41686     validateBlur : function(){
41687         return !this.list || !this.list.isVisible();   
41688     },
41689
41690     // private
41691     initQuery : function(){
41692         this.doQuery(this.getRawValue());
41693     },
41694
41695     // private
41696     doForce : function(){
41697         if(this.el.dom.value.length > 0){
41698             this.el.dom.value =
41699                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41700              
41701         }
41702     },
41703
41704     /**
41705      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41706      * query allowing the query action to be canceled if needed.
41707      * @param {String} query The SQL query to execute
41708      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41709      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41710      * saved in the current store (defaults to false)
41711      */
41712     doQuery : function(q, forceAll){
41713         if(q === undefined || q === null){
41714             q = '';
41715         }
41716         var qe = {
41717             query: q,
41718             forceAll: forceAll,
41719             combo: this,
41720             cancel:false
41721         };
41722         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41723             return false;
41724         }
41725         q = qe.query;
41726         forceAll = qe.forceAll;
41727         if(forceAll === true || (q.length >= this.minChars)){
41728             if(this.lastQuery != q || this.alwaysQuery){
41729                 this.lastQuery = q;
41730                 if(this.mode == 'local'){
41731                     this.selectedIndex = -1;
41732                     if(forceAll){
41733                         this.store.clearFilter();
41734                     }else{
41735                         this.store.filter(this.displayField, q);
41736                     }
41737                     this.onLoad();
41738                 }else{
41739                     this.store.baseParams[this.queryParam] = q;
41740                     this.store.load({
41741                         params: this.getParams(q)
41742                     });
41743                     this.expand();
41744                 }
41745             }else{
41746                 this.selectedIndex = -1;
41747                 this.onLoad();   
41748             }
41749         }
41750     },
41751
41752     // private
41753     getParams : function(q){
41754         var p = {};
41755         //p[this.queryParam] = q;
41756         if(this.pageSize){
41757             p.start = 0;
41758             p.limit = this.pageSize;
41759         }
41760         return p;
41761     },
41762
41763     /**
41764      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41765      */
41766     collapse : function(){
41767         if(!this.isExpanded()){
41768             return;
41769         }
41770         this.list.hide();
41771         Roo.get(document).un('mousedown', this.collapseIf, this);
41772         Roo.get(document).un('mousewheel', this.collapseIf, this);
41773         if (!this.editable) {
41774             Roo.get(document).un('keydown', this.listKeyPress, this);
41775         }
41776         this.fireEvent('collapse', this);
41777     },
41778
41779     // private
41780     collapseIf : function(e){
41781         if(!e.within(this.wrap) && !e.within(this.list)){
41782             this.collapse();
41783         }
41784     },
41785
41786     /**
41787      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41788      */
41789     expand : function(){
41790         if(this.isExpanded() || !this.hasFocus){
41791             return;
41792         }
41793         this.list.alignTo(this.el, this.listAlign);
41794         this.list.show();
41795         Roo.get(document).on('mousedown', this.collapseIf, this);
41796         Roo.get(document).on('mousewheel', this.collapseIf, this);
41797         if (!this.editable) {
41798             Roo.get(document).on('keydown', this.listKeyPress, this);
41799         }
41800         
41801         this.fireEvent('expand', this);
41802     },
41803
41804     // private
41805     // Implements the default empty TriggerField.onTriggerClick function
41806     onTriggerClick : function(){
41807         if(this.disabled){
41808             return;
41809         }
41810         if(this.isExpanded()){
41811             this.collapse();
41812             if (!this.blockFocus) {
41813                 this.el.focus();
41814             }
41815             
41816         }else {
41817             this.hasFocus = true;
41818             if(this.triggerAction == 'all') {
41819                 this.doQuery(this.allQuery, true);
41820             } else {
41821                 this.doQuery(this.getRawValue());
41822             }
41823             if (!this.blockFocus) {
41824                 this.el.focus();
41825             }
41826         }
41827     },
41828     listKeyPress : function(e)
41829     {
41830         //Roo.log('listkeypress');
41831         // scroll to first matching element based on key pres..
41832         if (e.isSpecialKey()) {
41833             return false;
41834         }
41835         var k = String.fromCharCode(e.getKey()).toUpperCase();
41836         //Roo.log(k);
41837         var match  = false;
41838         var csel = this.view.getSelectedNodes();
41839         var cselitem = false;
41840         if (csel.length) {
41841             var ix = this.view.indexOf(csel[0]);
41842             cselitem  = this.store.getAt(ix);
41843             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41844                 cselitem = false;
41845             }
41846             
41847         }
41848         
41849         this.store.each(function(v) { 
41850             if (cselitem) {
41851                 // start at existing selection.
41852                 if (cselitem.id == v.id) {
41853                     cselitem = false;
41854                 }
41855                 return;
41856             }
41857                 
41858             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41859                 match = this.store.indexOf(v);
41860                 return false;
41861             }
41862         }, this);
41863         
41864         if (match === false) {
41865             return true; // no more action?
41866         }
41867         // scroll to?
41868         this.view.select(match);
41869         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41870         sn.scrollIntoView(sn.dom.parentNode, false);
41871     }
41872
41873     /** 
41874     * @cfg {Boolean} grow 
41875     * @hide 
41876     */
41877     /** 
41878     * @cfg {Number} growMin 
41879     * @hide 
41880     */
41881     /** 
41882     * @cfg {Number} growMax 
41883     * @hide 
41884     */
41885     /**
41886      * @hide
41887      * @method autoSize
41888      */
41889 });/*
41890  * Copyright(c) 2010-2012, Roo J Solutions Limited
41891  *
41892  * Licence LGPL
41893  *
41894  */
41895
41896 /**
41897  * @class Roo.form.ComboBoxArray
41898  * @extends Roo.form.TextField
41899  * A facebook style adder... for lists of email / people / countries  etc...
41900  * pick multiple items from a combo box, and shows each one.
41901  *
41902  *  Fred [x]  Brian [x]  [Pick another |v]
41903  *
41904  *
41905  *  For this to work: it needs various extra information
41906  *    - normal combo problay has
41907  *      name, hiddenName
41908  *    + displayField, valueField
41909  *
41910  *    For our purpose...
41911  *
41912  *
41913  *   If we change from 'extends' to wrapping...
41914  *   
41915  *  
41916  *
41917  
41918  
41919  * @constructor
41920  * Create a new ComboBoxArray.
41921  * @param {Object} config Configuration options
41922  */
41923  
41924
41925 Roo.form.ComboBoxArray = function(config)
41926 {
41927     this.addEvents({
41928         /**
41929          * @event beforeremove
41930          * Fires before remove the value from the list
41931              * @param {Roo.form.ComboBoxArray} _self This combo box array
41932              * @param {Roo.form.ComboBoxArray.Item} item removed item
41933              */
41934         'beforeremove' : true,
41935         /**
41936          * @event remove
41937          * Fires when remove the value from the list
41938              * @param {Roo.form.ComboBoxArray} _self This combo box array
41939              * @param {Roo.form.ComboBoxArray.Item} item removed item
41940              */
41941         'remove' : true
41942         
41943         
41944     });
41945     
41946     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41947     
41948     this.items = new Roo.util.MixedCollection(false);
41949     
41950     // construct the child combo...
41951     
41952     
41953     
41954     
41955    
41956     
41957 }
41958
41959  
41960 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41961
41962     /**
41963      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41964      */
41965     
41966     lastData : false,
41967     
41968     // behavies liek a hiddne field
41969     inputType:      'hidden',
41970     /**
41971      * @cfg {Number} width The width of the box that displays the selected element
41972      */ 
41973     width:          300,
41974
41975     
41976     
41977     /**
41978      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
41979      */
41980     name : false,
41981     /**
41982      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
41983      */
41984     hiddenName : false,
41985     
41986     
41987     // private the array of items that are displayed..
41988     items  : false,
41989     // private - the hidden field el.
41990     hiddenEl : false,
41991     // private - the filed el..
41992     el : false,
41993     
41994     //validateValue : function() { return true; }, // all values are ok!
41995     //onAddClick: function() { },
41996     
41997     onRender : function(ct, position) 
41998     {
41999         
42000         // create the standard hidden element
42001         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42002         
42003         
42004         // give fake names to child combo;
42005         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42006         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42007         
42008         this.combo = Roo.factory(this.combo, Roo.form);
42009         this.combo.onRender(ct, position);
42010         if (typeof(this.combo.width) != 'undefined') {
42011             this.combo.onResize(this.combo.width,0);
42012         }
42013         
42014         this.combo.initEvents();
42015         
42016         // assigned so form know we need to do this..
42017         this.store          = this.combo.store;
42018         this.valueField     = this.combo.valueField;
42019         this.displayField   = this.combo.displayField ;
42020         
42021         
42022         this.combo.wrap.addClass('x-cbarray-grp');
42023         
42024         var cbwrap = this.combo.wrap.createChild(
42025             {tag: 'div', cls: 'x-cbarray-cb'},
42026             this.combo.el.dom
42027         );
42028         
42029              
42030         this.hiddenEl = this.combo.wrap.createChild({
42031             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42032         });
42033         this.el = this.combo.wrap.createChild({
42034             tag: 'input',  type:'hidden' , name: this.name, value : ''
42035         });
42036          //   this.el.dom.removeAttribute("name");
42037         
42038         
42039         this.outerWrap = this.combo.wrap;
42040         this.wrap = cbwrap;
42041         
42042         this.outerWrap.setWidth(this.width);
42043         this.outerWrap.dom.removeChild(this.el.dom);
42044         
42045         this.wrap.dom.appendChild(this.el.dom);
42046         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42047         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42048         
42049         this.combo.trigger.setStyle('position','relative');
42050         this.combo.trigger.setStyle('left', '0px');
42051         this.combo.trigger.setStyle('top', '2px');
42052         
42053         this.combo.el.setStyle('vertical-align', 'text-bottom');
42054         
42055         //this.trigger.setStyle('vertical-align', 'top');
42056         
42057         // this should use the code from combo really... on('add' ....)
42058         if (this.adder) {
42059             
42060         
42061             this.adder = this.outerWrap.createChild(
42062                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42063             var _t = this;
42064             this.adder.on('click', function(e) {
42065                 _t.fireEvent('adderclick', this, e);
42066             }, _t);
42067         }
42068         //var _t = this;
42069         //this.adder.on('click', this.onAddClick, _t);
42070         
42071         
42072         this.combo.on('select', function(cb, rec, ix) {
42073             this.addItem(rec.data);
42074             
42075             cb.setValue('');
42076             cb.el.dom.value = '';
42077             //cb.lastData = rec.data;
42078             // add to list
42079             
42080         }, this);
42081         
42082         
42083     },
42084     
42085     
42086     getName: function()
42087     {
42088         // returns hidden if it's set..
42089         if (!this.rendered) {return ''};
42090         return  this.hiddenName ? this.hiddenName : this.name;
42091         
42092     },
42093     
42094     
42095     onResize: function(w, h){
42096         
42097         return;
42098         // not sure if this is needed..
42099         //this.combo.onResize(w,h);
42100         
42101         if(typeof w != 'number'){
42102             // we do not handle it!?!?
42103             return;
42104         }
42105         var tw = this.combo.trigger.getWidth();
42106         tw += this.addicon ? this.addicon.getWidth() : 0;
42107         tw += this.editicon ? this.editicon.getWidth() : 0;
42108         var x = w - tw;
42109         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42110             
42111         this.combo.trigger.setStyle('left', '0px');
42112         
42113         if(this.list && this.listWidth === undefined){
42114             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42115             this.list.setWidth(lw);
42116             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42117         }
42118         
42119     
42120         
42121     },
42122     
42123     addItem: function(rec)
42124     {
42125         var valueField = this.combo.valueField;
42126         var displayField = this.combo.displayField;
42127         if (this.items.indexOfKey(rec[valueField]) > -1) {
42128             //console.log("GOT " + rec.data.id);
42129             return;
42130         }
42131         
42132         var x = new Roo.form.ComboBoxArray.Item({
42133             //id : rec[this.idField],
42134             data : rec,
42135             displayField : displayField ,
42136             tipField : displayField ,
42137             cb : this
42138         });
42139         // use the 
42140         this.items.add(rec[valueField],x);
42141         // add it before the element..
42142         this.updateHiddenEl();
42143         x.render(this.outerWrap, this.wrap.dom);
42144         // add the image handler..
42145     },
42146     
42147     updateHiddenEl : function()
42148     {
42149         this.validate();
42150         if (!this.hiddenEl) {
42151             return;
42152         }
42153         var ar = [];
42154         var idField = this.combo.valueField;
42155         
42156         this.items.each(function(f) {
42157             ar.push(f.data[idField]);
42158            
42159         });
42160         this.hiddenEl.dom.value = ar.join(',');
42161         this.validate();
42162     },
42163     
42164     reset : function()
42165     {
42166         this.items.clear();
42167         
42168         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42169            el.remove();
42170         });
42171         
42172         this.el.dom.value = '';
42173         if (this.hiddenEl) {
42174             this.hiddenEl.dom.value = '';
42175         }
42176         
42177     },
42178     getValue: function()
42179     {
42180         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42181     },
42182     setValue: function(v) // not a valid action - must use addItems..
42183     {
42184          
42185         this.reset();
42186         
42187         
42188         
42189         if (this.store.isLocal && (typeof(v) == 'string')) {
42190             // then we can use the store to find the values..
42191             // comma seperated at present.. this needs to allow JSON based encoding..
42192             this.hiddenEl.value  = v;
42193             var v_ar = [];
42194             Roo.each(v.split(','), function(k) {
42195                 Roo.log("CHECK " + this.valueField + ',' + k);
42196                 var li = this.store.query(this.valueField, k);
42197                 if (!li.length) {
42198                     return;
42199                 }
42200                 var add = {};
42201                 add[this.valueField] = k;
42202                 add[this.displayField] = li.item(0).data[this.displayField];
42203                 
42204                 this.addItem(add);
42205             }, this) 
42206              
42207         }
42208         if (typeof(v) == 'object' ) {
42209             // then let's assume it's an array of objects..
42210             Roo.each(v, function(l) {
42211                 this.addItem(l);
42212             }, this);
42213              
42214         }
42215         
42216         
42217     },
42218     setFromData: function(v)
42219     {
42220         // this recieves an object, if setValues is called.
42221         this.reset();
42222         this.el.dom.value = v[this.displayField];
42223         this.hiddenEl.dom.value = v[this.valueField];
42224         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42225             return;
42226         }
42227         var kv = v[this.valueField];
42228         var dv = v[this.displayField];
42229         kv = typeof(kv) != 'string' ? '' : kv;
42230         dv = typeof(dv) != 'string' ? '' : dv;
42231         
42232         
42233         var keys = kv.split(',');
42234         var display = dv.split(',');
42235         for (var i = 0 ; i < keys.length; i++) {
42236             
42237             add = {};
42238             add[this.valueField] = keys[i];
42239             add[this.displayField] = display[i];
42240             this.addItem(add);
42241         }
42242       
42243         
42244     },
42245     
42246     /**
42247      * Validates the combox array value
42248      * @return {Boolean} True if the value is valid, else false
42249      */
42250     validate : function(){
42251         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42252             this.clearInvalid();
42253             return true;
42254         }
42255         return false;
42256     },
42257     
42258     validateValue : function(value){
42259         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42260         
42261     },
42262     
42263     /*@
42264      * overide
42265      * 
42266      */
42267     isDirty : function() {
42268         if(this.disabled) {
42269             return false;
42270         }
42271         
42272         try {
42273             var d = Roo.decode(String(this.originalValue));
42274         } catch (e) {
42275             return String(this.getValue()) !== String(this.originalValue);
42276         }
42277         
42278         var originalValue = [];
42279         
42280         for (var i = 0; i < d.length; i++){
42281             originalValue.push(d[i][this.valueField]);
42282         }
42283         
42284         return String(this.getValue()) !== String(originalValue.join(','));
42285         
42286     }
42287     
42288 });
42289
42290
42291
42292 /**
42293  * @class Roo.form.ComboBoxArray.Item
42294  * @extends Roo.BoxComponent
42295  * A selected item in the list
42296  *  Fred [x]  Brian [x]  [Pick another |v]
42297  * 
42298  * @constructor
42299  * Create a new item.
42300  * @param {Object} config Configuration options
42301  */
42302  
42303 Roo.form.ComboBoxArray.Item = function(config) {
42304     config.id = Roo.id();
42305     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42306 }
42307
42308 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42309     data : {},
42310     cb: false,
42311     displayField : false,
42312     tipField : false,
42313     
42314     
42315     defaultAutoCreate : {
42316         tag: 'div',
42317         cls: 'x-cbarray-item',
42318         cn : [ 
42319             { tag: 'div' },
42320             {
42321                 tag: 'img',
42322                 width:16,
42323                 height : 16,
42324                 src : Roo.BLANK_IMAGE_URL ,
42325                 align: 'center'
42326             }
42327         ]
42328         
42329     },
42330     
42331  
42332     onRender : function(ct, position)
42333     {
42334         Roo.form.Field.superclass.onRender.call(this, ct, position);
42335         
42336         if(!this.el){
42337             var cfg = this.getAutoCreate();
42338             this.el = ct.createChild(cfg, position);
42339         }
42340         
42341         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42342         
42343         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42344             this.cb.renderer(this.data) :
42345             String.format('{0}',this.data[this.displayField]);
42346         
42347             
42348         this.el.child('div').dom.setAttribute('qtip',
42349                         String.format('{0}',this.data[this.tipField])
42350         );
42351         
42352         this.el.child('img').on('click', this.remove, this);
42353         
42354     },
42355    
42356     remove : function()
42357     {
42358         if(this.cb.disabled){
42359             return;
42360         }
42361         
42362         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42363             this.cb.items.remove(this);
42364             this.el.child('img').un('click', this.remove, this);
42365             this.el.remove();
42366             this.cb.updateHiddenEl();
42367
42368             this.cb.fireEvent('remove', this.cb, this);
42369         }
42370         
42371     }
42372 });/*
42373  * Based on:
42374  * Ext JS Library 1.1.1
42375  * Copyright(c) 2006-2007, Ext JS, LLC.
42376  *
42377  * Originally Released Under LGPL - original licence link has changed is not relivant.
42378  *
42379  * Fork - LGPL
42380  * <script type="text/javascript">
42381  */
42382 /**
42383  * @class Roo.form.Checkbox
42384  * @extends Roo.form.Field
42385  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42386  * @constructor
42387  * Creates a new Checkbox
42388  * @param {Object} config Configuration options
42389  */
42390 Roo.form.Checkbox = function(config){
42391     Roo.form.Checkbox.superclass.constructor.call(this, config);
42392     this.addEvents({
42393         /**
42394          * @event check
42395          * Fires when the checkbox is checked or unchecked.
42396              * @param {Roo.form.Checkbox} this This checkbox
42397              * @param {Boolean} checked The new checked value
42398              */
42399         check : true
42400     });
42401 };
42402
42403 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42404     /**
42405      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42406      */
42407     focusClass : undefined,
42408     /**
42409      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42410      */
42411     fieldClass: "x-form-field",
42412     /**
42413      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42414      */
42415     checked: false,
42416     /**
42417      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42418      * {tag: "input", type: "checkbox", autocomplete: "off"})
42419      */
42420     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42421     /**
42422      * @cfg {String} boxLabel The text that appears beside the checkbox
42423      */
42424     boxLabel : "",
42425     /**
42426      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42427      */  
42428     inputValue : '1',
42429     /**
42430      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42431      */
42432      valueOff: '0', // value when not checked..
42433
42434     actionMode : 'viewEl', 
42435     //
42436     // private
42437     itemCls : 'x-menu-check-item x-form-item',
42438     groupClass : 'x-menu-group-item',
42439     inputType : 'hidden',
42440     
42441     
42442     inSetChecked: false, // check that we are not calling self...
42443     
42444     inputElement: false, // real input element?
42445     basedOn: false, // ????
42446     
42447     isFormField: true, // not sure where this is needed!!!!
42448
42449     onResize : function(){
42450         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42451         if(!this.boxLabel){
42452             this.el.alignTo(this.wrap, 'c-c');
42453         }
42454     },
42455
42456     initEvents : function(){
42457         Roo.form.Checkbox.superclass.initEvents.call(this);
42458         this.el.on("click", this.onClick,  this);
42459         this.el.on("change", this.onClick,  this);
42460     },
42461
42462
42463     getResizeEl : function(){
42464         return this.wrap;
42465     },
42466
42467     getPositionEl : function(){
42468         return this.wrap;
42469     },
42470
42471     // private
42472     onRender : function(ct, position){
42473         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42474         /*
42475         if(this.inputValue !== undefined){
42476             this.el.dom.value = this.inputValue;
42477         }
42478         */
42479         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42480         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42481         var viewEl = this.wrap.createChild({ 
42482             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42483         this.viewEl = viewEl;   
42484         this.wrap.on('click', this.onClick,  this); 
42485         
42486         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42487         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42488         
42489         
42490         
42491         if(this.boxLabel){
42492             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42493         //    viewEl.on('click', this.onClick,  this); 
42494         }
42495         //if(this.checked){
42496             this.setChecked(this.checked);
42497         //}else{
42498             //this.checked = this.el.dom;
42499         //}
42500
42501     },
42502
42503     // private
42504     initValue : Roo.emptyFn,
42505
42506     /**
42507      * Returns the checked state of the checkbox.
42508      * @return {Boolean} True if checked, else false
42509      */
42510     getValue : function(){
42511         if(this.el){
42512             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42513         }
42514         return this.valueOff;
42515         
42516     },
42517
42518         // private
42519     onClick : function(){ 
42520         if (this.disabled) {
42521             return;
42522         }
42523         this.setChecked(!this.checked);
42524
42525         //if(this.el.dom.checked != this.checked){
42526         //    this.setValue(this.el.dom.checked);
42527        // }
42528     },
42529
42530     /**
42531      * Sets the checked state of the checkbox.
42532      * On is always based on a string comparison between inputValue and the param.
42533      * @param {Boolean/String} value - the value to set 
42534      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42535      */
42536     setValue : function(v,suppressEvent){
42537         
42538         
42539         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42540         //if(this.el && this.el.dom){
42541         //    this.el.dom.checked = this.checked;
42542         //    this.el.dom.defaultChecked = this.checked;
42543         //}
42544         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42545         //this.fireEvent("check", this, this.checked);
42546     },
42547     // private..
42548     setChecked : function(state,suppressEvent)
42549     {
42550         if (this.inSetChecked) {
42551             this.checked = state;
42552             return;
42553         }
42554         
42555     
42556         if(this.wrap){
42557             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42558         }
42559         this.checked = state;
42560         if(suppressEvent !== true){
42561             this.fireEvent('check', this, state);
42562         }
42563         this.inSetChecked = true;
42564         this.el.dom.value = state ? this.inputValue : this.valueOff;
42565         this.inSetChecked = false;
42566         
42567     },
42568     // handle setting of hidden value by some other method!!?!?
42569     setFromHidden: function()
42570     {
42571         if(!this.el){
42572             return;
42573         }
42574         //console.log("SET FROM HIDDEN");
42575         //alert('setFrom hidden');
42576         this.setValue(this.el.dom.value);
42577     },
42578     
42579     onDestroy : function()
42580     {
42581         if(this.viewEl){
42582             Roo.get(this.viewEl).remove();
42583         }
42584          
42585         Roo.form.Checkbox.superclass.onDestroy.call(this);
42586     },
42587     
42588     setBoxLabel : function(str)
42589     {
42590         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42591     }
42592
42593 });/*
42594  * Based on:
42595  * Ext JS Library 1.1.1
42596  * Copyright(c) 2006-2007, Ext JS, LLC.
42597  *
42598  * Originally Released Under LGPL - original licence link has changed is not relivant.
42599  *
42600  * Fork - LGPL
42601  * <script type="text/javascript">
42602  */
42603  
42604 /**
42605  * @class Roo.form.Radio
42606  * @extends Roo.form.Checkbox
42607  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42608  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42609  * @constructor
42610  * Creates a new Radio
42611  * @param {Object} config Configuration options
42612  */
42613 Roo.form.Radio = function(){
42614     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42615 };
42616 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42617     inputType: 'radio',
42618
42619     /**
42620      * If this radio is part of a group, it will return the selected value
42621      * @return {String}
42622      */
42623     getGroupValue : function(){
42624         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42625     },
42626     
42627     
42628     onRender : function(ct, position){
42629         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42630         
42631         if(this.inputValue !== undefined){
42632             this.el.dom.value = this.inputValue;
42633         }
42634          
42635         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42636         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42637         //var viewEl = this.wrap.createChild({ 
42638         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42639         //this.viewEl = viewEl;   
42640         //this.wrap.on('click', this.onClick,  this); 
42641         
42642         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42643         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42644         
42645         
42646         
42647         if(this.boxLabel){
42648             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42649         //    viewEl.on('click', this.onClick,  this); 
42650         }
42651          if(this.checked){
42652             this.el.dom.checked =   'checked' ;
42653         }
42654          
42655     } 
42656     
42657     
42658 });//<script type="text/javascript">
42659
42660 /*
42661  * Based  Ext JS Library 1.1.1
42662  * Copyright(c) 2006-2007, Ext JS, LLC.
42663  * LGPL
42664  *
42665  */
42666  
42667 /**
42668  * @class Roo.HtmlEditorCore
42669  * @extends Roo.Component
42670  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42671  *
42672  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42673  */
42674
42675 Roo.HtmlEditorCore = function(config){
42676     
42677     
42678     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42679     
42680     
42681     this.addEvents({
42682         /**
42683          * @event initialize
42684          * Fires when the editor is fully initialized (including the iframe)
42685          * @param {Roo.HtmlEditorCore} this
42686          */
42687         initialize: true,
42688         /**
42689          * @event activate
42690          * Fires when the editor is first receives the focus. Any insertion must wait
42691          * until after this event.
42692          * @param {Roo.HtmlEditorCore} this
42693          */
42694         activate: true,
42695          /**
42696          * @event beforesync
42697          * Fires before the textarea is updated with content from the editor iframe. Return false
42698          * to cancel the sync.
42699          * @param {Roo.HtmlEditorCore} this
42700          * @param {String} html
42701          */
42702         beforesync: true,
42703          /**
42704          * @event beforepush
42705          * Fires before the iframe editor is updated with content from the textarea. Return false
42706          * to cancel the push.
42707          * @param {Roo.HtmlEditorCore} this
42708          * @param {String} html
42709          */
42710         beforepush: true,
42711          /**
42712          * @event sync
42713          * Fires when the textarea is updated with content from the editor iframe.
42714          * @param {Roo.HtmlEditorCore} this
42715          * @param {String} html
42716          */
42717         sync: true,
42718          /**
42719          * @event push
42720          * Fires when the iframe editor is updated with content from the textarea.
42721          * @param {Roo.HtmlEditorCore} this
42722          * @param {String} html
42723          */
42724         push: true,
42725         
42726         /**
42727          * @event editorevent
42728          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42729          * @param {Roo.HtmlEditorCore} this
42730          */
42731         editorevent: true
42732         
42733     });
42734     
42735     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42736     
42737     // defaults : white / black...
42738     this.applyBlacklists();
42739     
42740     
42741     
42742 };
42743
42744
42745 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42746
42747
42748      /**
42749      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42750      */
42751     
42752     owner : false,
42753     
42754      /**
42755      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42756      *                        Roo.resizable.
42757      */
42758     resizable : false,
42759      /**
42760      * @cfg {Number} height (in pixels)
42761      */   
42762     height: 300,
42763    /**
42764      * @cfg {Number} width (in pixels)
42765      */   
42766     width: 500,
42767     
42768     /**
42769      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42770      * 
42771      */
42772     stylesheets: false,
42773     
42774     // id of frame..
42775     frameId: false,
42776     
42777     // private properties
42778     validationEvent : false,
42779     deferHeight: true,
42780     initialized : false,
42781     activated : false,
42782     sourceEditMode : false,
42783     onFocus : Roo.emptyFn,
42784     iframePad:3,
42785     hideMode:'offsets',
42786     
42787     clearUp: true,
42788     
42789     // blacklist + whitelisted elements..
42790     black: false,
42791     white: false,
42792      
42793     
42794
42795     /**
42796      * Protected method that will not generally be called directly. It
42797      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42798      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42799      */
42800     getDocMarkup : function(){
42801         // body styles..
42802         var st = '';
42803         
42804         // inherit styels from page...?? 
42805         if (this.stylesheets === false) {
42806             
42807             Roo.get(document.head).select('style').each(function(node) {
42808                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42809             });
42810             
42811             Roo.get(document.head).select('link').each(function(node) { 
42812                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42813             });
42814             
42815         } else if (!this.stylesheets.length) {
42816                 // simple..
42817                 st = '<style type="text/css">' +
42818                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42819                    '</style>';
42820         } else { 
42821             
42822         }
42823         
42824         st +=  '<style type="text/css">' +
42825             'IMG { cursor: pointer } ' +
42826         '</style>';
42827
42828         
42829         return '<html><head>' + st  +
42830             //<style type="text/css">' +
42831             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42832             //'</style>' +
42833             ' </head><body class="roo-htmleditor-body"></body></html>';
42834     },
42835
42836     // private
42837     onRender : function(ct, position)
42838     {
42839         var _t = this;
42840         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42841         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42842         
42843         
42844         this.el.dom.style.border = '0 none';
42845         this.el.dom.setAttribute('tabIndex', -1);
42846         this.el.addClass('x-hidden hide');
42847         
42848         
42849         
42850         if(Roo.isIE){ // fix IE 1px bogus margin
42851             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42852         }
42853        
42854         
42855         this.frameId = Roo.id();
42856         
42857          
42858         
42859         var iframe = this.owner.wrap.createChild({
42860             tag: 'iframe',
42861             cls: 'form-control', // bootstrap..
42862             id: this.frameId,
42863             name: this.frameId,
42864             frameBorder : 'no',
42865             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42866         }, this.el
42867         );
42868         
42869         
42870         this.iframe = iframe.dom;
42871
42872          this.assignDocWin();
42873         
42874         this.doc.designMode = 'on';
42875        
42876         this.doc.open();
42877         this.doc.write(this.getDocMarkup());
42878         this.doc.close();
42879
42880         
42881         var task = { // must defer to wait for browser to be ready
42882             run : function(){
42883                 //console.log("run task?" + this.doc.readyState);
42884                 this.assignDocWin();
42885                 if(this.doc.body || this.doc.readyState == 'complete'){
42886                     try {
42887                         this.doc.designMode="on";
42888                     } catch (e) {
42889                         return;
42890                     }
42891                     Roo.TaskMgr.stop(task);
42892                     this.initEditor.defer(10, this);
42893                 }
42894             },
42895             interval : 10,
42896             duration: 10000,
42897             scope: this
42898         };
42899         Roo.TaskMgr.start(task);
42900
42901     },
42902
42903     // private
42904     onResize : function(w, h)
42905     {
42906          Roo.log('resize: ' +w + ',' + h );
42907         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42908         if(!this.iframe){
42909             return;
42910         }
42911         if(typeof w == 'number'){
42912             
42913             this.iframe.style.width = w + 'px';
42914         }
42915         if(typeof h == 'number'){
42916             
42917             this.iframe.style.height = h + 'px';
42918             if(this.doc){
42919                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42920             }
42921         }
42922         
42923     },
42924
42925     /**
42926      * Toggles the editor between standard and source edit mode.
42927      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42928      */
42929     toggleSourceEdit : function(sourceEditMode){
42930         
42931         this.sourceEditMode = sourceEditMode === true;
42932         
42933         if(this.sourceEditMode){
42934  
42935             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42936             
42937         }else{
42938             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42939             //this.iframe.className = '';
42940             this.deferFocus();
42941         }
42942         //this.setSize(this.owner.wrap.getSize());
42943         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42944     },
42945
42946     
42947   
42948
42949     /**
42950      * Protected method that will not generally be called directly. If you need/want
42951      * custom HTML cleanup, this is the method you should override.
42952      * @param {String} html The HTML to be cleaned
42953      * return {String} The cleaned HTML
42954      */
42955     cleanHtml : function(html){
42956         html = String(html);
42957         if(html.length > 5){
42958             if(Roo.isSafari){ // strip safari nonsense
42959                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42960             }
42961         }
42962         if(html == '&nbsp;'){
42963             html = '';
42964         }
42965         return html;
42966     },
42967
42968     /**
42969      * HTML Editor -> Textarea
42970      * Protected method that will not generally be called directly. Syncs the contents
42971      * of the editor iframe with the textarea.
42972      */
42973     syncValue : function(){
42974         if(this.initialized){
42975             var bd = (this.doc.body || this.doc.documentElement);
42976             //this.cleanUpPaste(); -- this is done else where and causes havoc..
42977             var html = bd.innerHTML;
42978             if(Roo.isSafari){
42979                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
42980                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
42981                 if(m && m[1]){
42982                     html = '<div style="'+m[0]+'">' + html + '</div>';
42983                 }
42984             }
42985             html = this.cleanHtml(html);
42986             // fix up the special chars.. normaly like back quotes in word...
42987             // however we do not want to do this with chinese..
42988             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
42989                 var cc = b.charCodeAt();
42990                 if (
42991                     (cc >= 0x4E00 && cc < 0xA000 ) ||
42992                     (cc >= 0x3400 && cc < 0x4E00 ) ||
42993                     (cc >= 0xf900 && cc < 0xfb00 )
42994                 ) {
42995                         return b;
42996                 }
42997                 return "&#"+cc+";" 
42998             });
42999             if(this.owner.fireEvent('beforesync', this, html) !== false){
43000                 this.el.dom.value = html;
43001                 this.owner.fireEvent('sync', this, html);
43002             }
43003         }
43004     },
43005
43006     /**
43007      * Protected method that will not generally be called directly. Pushes the value of the textarea
43008      * into the iframe editor.
43009      */
43010     pushValue : function(){
43011         if(this.initialized){
43012             var v = this.el.dom.value.trim();
43013             
43014 //            if(v.length < 1){
43015 //                v = '&#160;';
43016 //            }
43017             
43018             if(this.owner.fireEvent('beforepush', this, v) !== false){
43019                 var d = (this.doc.body || this.doc.documentElement);
43020                 d.innerHTML = v;
43021                 this.cleanUpPaste();
43022                 this.el.dom.value = d.innerHTML;
43023                 this.owner.fireEvent('push', this, v);
43024             }
43025         }
43026     },
43027
43028     // private
43029     deferFocus : function(){
43030         this.focus.defer(10, this);
43031     },
43032
43033     // doc'ed in Field
43034     focus : function(){
43035         if(this.win && !this.sourceEditMode){
43036             this.win.focus();
43037         }else{
43038             this.el.focus();
43039         }
43040     },
43041     
43042     assignDocWin: function()
43043     {
43044         var iframe = this.iframe;
43045         
43046          if(Roo.isIE){
43047             this.doc = iframe.contentWindow.document;
43048             this.win = iframe.contentWindow;
43049         } else {
43050 //            if (!Roo.get(this.frameId)) {
43051 //                return;
43052 //            }
43053 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43054 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43055             
43056             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43057                 return;
43058             }
43059             
43060             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43061             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43062         }
43063     },
43064     
43065     // private
43066     initEditor : function(){
43067         //console.log("INIT EDITOR");
43068         this.assignDocWin();
43069         
43070         
43071         
43072         this.doc.designMode="on";
43073         this.doc.open();
43074         this.doc.write(this.getDocMarkup());
43075         this.doc.close();
43076         
43077         var dbody = (this.doc.body || this.doc.documentElement);
43078         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43079         // this copies styles from the containing element into thsi one..
43080         // not sure why we need all of this..
43081         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43082         
43083         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43084         //ss['background-attachment'] = 'fixed'; // w3c
43085         dbody.bgProperties = 'fixed'; // ie
43086         //Roo.DomHelper.applyStyles(dbody, ss);
43087         Roo.EventManager.on(this.doc, {
43088             //'mousedown': this.onEditorEvent,
43089             'mouseup': this.onEditorEvent,
43090             'dblclick': this.onEditorEvent,
43091             'click': this.onEditorEvent,
43092             'keyup': this.onEditorEvent,
43093             buffer:100,
43094             scope: this
43095         });
43096         if(Roo.isGecko){
43097             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43098         }
43099         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43100             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43101         }
43102         this.initialized = true;
43103
43104         this.owner.fireEvent('initialize', this);
43105         this.pushValue();
43106     },
43107
43108     // private
43109     onDestroy : function(){
43110         
43111         
43112         
43113         if(this.rendered){
43114             
43115             //for (var i =0; i < this.toolbars.length;i++) {
43116             //    // fixme - ask toolbars for heights?
43117             //    this.toolbars[i].onDestroy();
43118            // }
43119             
43120             //this.wrap.dom.innerHTML = '';
43121             //this.wrap.remove();
43122         }
43123     },
43124
43125     // private
43126     onFirstFocus : function(){
43127         
43128         this.assignDocWin();
43129         
43130         
43131         this.activated = true;
43132          
43133     
43134         if(Roo.isGecko){ // prevent silly gecko errors
43135             this.win.focus();
43136             var s = this.win.getSelection();
43137             if(!s.focusNode || s.focusNode.nodeType != 3){
43138                 var r = s.getRangeAt(0);
43139                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43140                 r.collapse(true);
43141                 this.deferFocus();
43142             }
43143             try{
43144                 this.execCmd('useCSS', true);
43145                 this.execCmd('styleWithCSS', false);
43146             }catch(e){}
43147         }
43148         this.owner.fireEvent('activate', this);
43149     },
43150
43151     // private
43152     adjustFont: function(btn){
43153         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43154         //if(Roo.isSafari){ // safari
43155         //    adjust *= 2;
43156        // }
43157         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43158         if(Roo.isSafari){ // safari
43159             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43160             v =  (v < 10) ? 10 : v;
43161             v =  (v > 48) ? 48 : v;
43162             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43163             
43164         }
43165         
43166         
43167         v = Math.max(1, v+adjust);
43168         
43169         this.execCmd('FontSize', v  );
43170     },
43171
43172     onEditorEvent : function(e)
43173     {
43174         this.owner.fireEvent('editorevent', this, e);
43175       //  this.updateToolbar();
43176         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43177     },
43178
43179     insertTag : function(tg)
43180     {
43181         // could be a bit smarter... -> wrap the current selected tRoo..
43182         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43183             
43184             range = this.createRange(this.getSelection());
43185             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43186             wrappingNode.appendChild(range.extractContents());
43187             range.insertNode(wrappingNode);
43188
43189             return;
43190             
43191             
43192             
43193         }
43194         this.execCmd("formatblock",   tg);
43195         
43196     },
43197     
43198     insertText : function(txt)
43199     {
43200         
43201         
43202         var range = this.createRange();
43203         range.deleteContents();
43204                //alert(Sender.getAttribute('label'));
43205                
43206         range.insertNode(this.doc.createTextNode(txt));
43207     } ,
43208     
43209      
43210
43211     /**
43212      * Executes a Midas editor command on the editor document and performs necessary focus and
43213      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43214      * @param {String} cmd The Midas command
43215      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43216      */
43217     relayCmd : function(cmd, value){
43218         this.win.focus();
43219         this.execCmd(cmd, value);
43220         this.owner.fireEvent('editorevent', this);
43221         //this.updateToolbar();
43222         this.owner.deferFocus();
43223     },
43224
43225     /**
43226      * Executes a Midas editor command directly on the editor document.
43227      * For visual commands, you should use {@link #relayCmd} instead.
43228      * <b>This should only be called after the editor is initialized.</b>
43229      * @param {String} cmd The Midas command
43230      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43231      */
43232     execCmd : function(cmd, value){
43233         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43234         this.syncValue();
43235     },
43236  
43237  
43238    
43239     /**
43240      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43241      * to insert tRoo.
43242      * @param {String} text | dom node.. 
43243      */
43244     insertAtCursor : function(text)
43245     {
43246         
43247         
43248         
43249         if(!this.activated){
43250             return;
43251         }
43252         /*
43253         if(Roo.isIE){
43254             this.win.focus();
43255             var r = this.doc.selection.createRange();
43256             if(r){
43257                 r.collapse(true);
43258                 r.pasteHTML(text);
43259                 this.syncValue();
43260                 this.deferFocus();
43261             
43262             }
43263             return;
43264         }
43265         */
43266         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43267             this.win.focus();
43268             
43269             
43270             // from jquery ui (MIT licenced)
43271             var range, node;
43272             var win = this.win;
43273             
43274             if (win.getSelection && win.getSelection().getRangeAt) {
43275                 range = win.getSelection().getRangeAt(0);
43276                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43277                 range.insertNode(node);
43278             } else if (win.document.selection && win.document.selection.createRange) {
43279                 // no firefox support
43280                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43281                 win.document.selection.createRange().pasteHTML(txt);
43282             } else {
43283                 // no firefox support
43284                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43285                 this.execCmd('InsertHTML', txt);
43286             } 
43287             
43288             this.syncValue();
43289             
43290             this.deferFocus();
43291         }
43292     },
43293  // private
43294     mozKeyPress : function(e){
43295         if(e.ctrlKey){
43296             var c = e.getCharCode(), cmd;
43297           
43298             if(c > 0){
43299                 c = String.fromCharCode(c).toLowerCase();
43300                 switch(c){
43301                     case 'b':
43302                         cmd = 'bold';
43303                         break;
43304                     case 'i':
43305                         cmd = 'italic';
43306                         break;
43307                     
43308                     case 'u':
43309                         cmd = 'underline';
43310                         break;
43311                     
43312                     case 'v':
43313                         this.cleanUpPaste.defer(100, this);
43314                         return;
43315                         
43316                 }
43317                 if(cmd){
43318                     this.win.focus();
43319                     this.execCmd(cmd);
43320                     this.deferFocus();
43321                     e.preventDefault();
43322                 }
43323                 
43324             }
43325         }
43326     },
43327
43328     // private
43329     fixKeys : function(){ // load time branching for fastest keydown performance
43330         if(Roo.isIE){
43331             return function(e){
43332                 var k = e.getKey(), r;
43333                 if(k == e.TAB){
43334                     e.stopEvent();
43335                     r = this.doc.selection.createRange();
43336                     if(r){
43337                         r.collapse(true);
43338                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43339                         this.deferFocus();
43340                     }
43341                     return;
43342                 }
43343                 
43344                 if(k == e.ENTER){
43345                     r = this.doc.selection.createRange();
43346                     if(r){
43347                         var target = r.parentElement();
43348                         if(!target || target.tagName.toLowerCase() != 'li'){
43349                             e.stopEvent();
43350                             r.pasteHTML('<br />');
43351                             r.collapse(false);
43352                             r.select();
43353                         }
43354                     }
43355                 }
43356                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43357                     this.cleanUpPaste.defer(100, this);
43358                     return;
43359                 }
43360                 
43361                 
43362             };
43363         }else if(Roo.isOpera){
43364             return function(e){
43365                 var k = e.getKey();
43366                 if(k == e.TAB){
43367                     e.stopEvent();
43368                     this.win.focus();
43369                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43370                     this.deferFocus();
43371                 }
43372                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43373                     this.cleanUpPaste.defer(100, this);
43374                     return;
43375                 }
43376                 
43377             };
43378         }else if(Roo.isSafari){
43379             return function(e){
43380                 var k = e.getKey();
43381                 
43382                 if(k == e.TAB){
43383                     e.stopEvent();
43384                     this.execCmd('InsertText','\t');
43385                     this.deferFocus();
43386                     return;
43387                 }
43388                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43389                     this.cleanUpPaste.defer(100, this);
43390                     return;
43391                 }
43392                 
43393              };
43394         }
43395     }(),
43396     
43397     getAllAncestors: function()
43398     {
43399         var p = this.getSelectedNode();
43400         var a = [];
43401         if (!p) {
43402             a.push(p); // push blank onto stack..
43403             p = this.getParentElement();
43404         }
43405         
43406         
43407         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43408             a.push(p);
43409             p = p.parentNode;
43410         }
43411         a.push(this.doc.body);
43412         return a;
43413     },
43414     lastSel : false,
43415     lastSelNode : false,
43416     
43417     
43418     getSelection : function() 
43419     {
43420         this.assignDocWin();
43421         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43422     },
43423     
43424     getSelectedNode: function() 
43425     {
43426         // this may only work on Gecko!!!
43427         
43428         // should we cache this!!!!
43429         
43430         
43431         
43432          
43433         var range = this.createRange(this.getSelection()).cloneRange();
43434         
43435         if (Roo.isIE) {
43436             var parent = range.parentElement();
43437             while (true) {
43438                 var testRange = range.duplicate();
43439                 testRange.moveToElementText(parent);
43440                 if (testRange.inRange(range)) {
43441                     break;
43442                 }
43443                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43444                     break;
43445                 }
43446                 parent = parent.parentElement;
43447             }
43448             return parent;
43449         }
43450         
43451         // is ancestor a text element.
43452         var ac =  range.commonAncestorContainer;
43453         if (ac.nodeType == 3) {
43454             ac = ac.parentNode;
43455         }
43456         
43457         var ar = ac.childNodes;
43458          
43459         var nodes = [];
43460         var other_nodes = [];
43461         var has_other_nodes = false;
43462         for (var i=0;i<ar.length;i++) {
43463             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43464                 continue;
43465             }
43466             // fullly contained node.
43467             
43468             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43469                 nodes.push(ar[i]);
43470                 continue;
43471             }
43472             
43473             // probably selected..
43474             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43475                 other_nodes.push(ar[i]);
43476                 continue;
43477             }
43478             // outer..
43479             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43480                 continue;
43481             }
43482             
43483             
43484             has_other_nodes = true;
43485         }
43486         if (!nodes.length && other_nodes.length) {
43487             nodes= other_nodes;
43488         }
43489         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43490             return false;
43491         }
43492         
43493         return nodes[0];
43494     },
43495     createRange: function(sel)
43496     {
43497         // this has strange effects when using with 
43498         // top toolbar - not sure if it's a great idea.
43499         //this.editor.contentWindow.focus();
43500         if (typeof sel != "undefined") {
43501             try {
43502                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43503             } catch(e) {
43504                 return this.doc.createRange();
43505             }
43506         } else {
43507             return this.doc.createRange();
43508         }
43509     },
43510     getParentElement: function()
43511     {
43512         
43513         this.assignDocWin();
43514         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43515         
43516         var range = this.createRange(sel);
43517          
43518         try {
43519             var p = range.commonAncestorContainer;
43520             while (p.nodeType == 3) { // text node
43521                 p = p.parentNode;
43522             }
43523             return p;
43524         } catch (e) {
43525             return null;
43526         }
43527     
43528     },
43529     /***
43530      *
43531      * Range intersection.. the hard stuff...
43532      *  '-1' = before
43533      *  '0' = hits..
43534      *  '1' = after.
43535      *         [ -- selected range --- ]
43536      *   [fail]                        [fail]
43537      *
43538      *    basically..
43539      *      if end is before start or  hits it. fail.
43540      *      if start is after end or hits it fail.
43541      *
43542      *   if either hits (but other is outside. - then it's not 
43543      *   
43544      *    
43545      **/
43546     
43547     
43548     // @see http://www.thismuchiknow.co.uk/?p=64.
43549     rangeIntersectsNode : function(range, node)
43550     {
43551         var nodeRange = node.ownerDocument.createRange();
43552         try {
43553             nodeRange.selectNode(node);
43554         } catch (e) {
43555             nodeRange.selectNodeContents(node);
43556         }
43557     
43558         var rangeStartRange = range.cloneRange();
43559         rangeStartRange.collapse(true);
43560     
43561         var rangeEndRange = range.cloneRange();
43562         rangeEndRange.collapse(false);
43563     
43564         var nodeStartRange = nodeRange.cloneRange();
43565         nodeStartRange.collapse(true);
43566     
43567         var nodeEndRange = nodeRange.cloneRange();
43568         nodeEndRange.collapse(false);
43569     
43570         return rangeStartRange.compareBoundaryPoints(
43571                  Range.START_TO_START, nodeEndRange) == -1 &&
43572                rangeEndRange.compareBoundaryPoints(
43573                  Range.START_TO_START, nodeStartRange) == 1;
43574         
43575          
43576     },
43577     rangeCompareNode : function(range, node)
43578     {
43579         var nodeRange = node.ownerDocument.createRange();
43580         try {
43581             nodeRange.selectNode(node);
43582         } catch (e) {
43583             nodeRange.selectNodeContents(node);
43584         }
43585         
43586         
43587         range.collapse(true);
43588     
43589         nodeRange.collapse(true);
43590      
43591         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43592         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43593          
43594         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43595         
43596         var nodeIsBefore   =  ss == 1;
43597         var nodeIsAfter    = ee == -1;
43598         
43599         if (nodeIsBefore && nodeIsAfter) {
43600             return 0; // outer
43601         }
43602         if (!nodeIsBefore && nodeIsAfter) {
43603             return 1; //right trailed.
43604         }
43605         
43606         if (nodeIsBefore && !nodeIsAfter) {
43607             return 2;  // left trailed.
43608         }
43609         // fully contined.
43610         return 3;
43611     },
43612
43613     // private? - in a new class?
43614     cleanUpPaste :  function()
43615     {
43616         // cleans up the whole document..
43617         Roo.log('cleanuppaste');
43618         
43619         this.cleanUpChildren(this.doc.body);
43620         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43621         if (clean != this.doc.body.innerHTML) {
43622             this.doc.body.innerHTML = clean;
43623         }
43624         
43625     },
43626     
43627     cleanWordChars : function(input) {// change the chars to hex code
43628         var he = Roo.HtmlEditorCore;
43629         
43630         var output = input;
43631         Roo.each(he.swapCodes, function(sw) { 
43632             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43633             
43634             output = output.replace(swapper, sw[1]);
43635         });
43636         
43637         return output;
43638     },
43639     
43640     
43641     cleanUpChildren : function (n)
43642     {
43643         if (!n.childNodes.length) {
43644             return;
43645         }
43646         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43647            this.cleanUpChild(n.childNodes[i]);
43648         }
43649     },
43650     
43651     
43652         
43653     
43654     cleanUpChild : function (node)
43655     {
43656         var ed = this;
43657         //console.log(node);
43658         if (node.nodeName == "#text") {
43659             // clean up silly Windows -- stuff?
43660             return; 
43661         }
43662         if (node.nodeName == "#comment") {
43663             node.parentNode.removeChild(node);
43664             // clean up silly Windows -- stuff?
43665             return; 
43666         }
43667         var lcname = node.tagName.toLowerCase();
43668         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43669         // whitelist of tags..
43670         
43671         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43672             // remove node.
43673             node.parentNode.removeChild(node);
43674             return;
43675             
43676         }
43677         
43678         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43679         
43680         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43681         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43682         
43683         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43684         //    remove_keep_children = true;
43685         //}
43686         
43687         if (remove_keep_children) {
43688             this.cleanUpChildren(node);
43689             // inserts everything just before this node...
43690             while (node.childNodes.length) {
43691                 var cn = node.childNodes[0];
43692                 node.removeChild(cn);
43693                 node.parentNode.insertBefore(cn, node);
43694             }
43695             node.parentNode.removeChild(node);
43696             return;
43697         }
43698         
43699         if (!node.attributes || !node.attributes.length) {
43700             this.cleanUpChildren(node);
43701             return;
43702         }
43703         
43704         function cleanAttr(n,v)
43705         {
43706             
43707             if (v.match(/^\./) || v.match(/^\//)) {
43708                 return;
43709             }
43710             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43711                 return;
43712             }
43713             if (v.match(/^#/)) {
43714                 return;
43715             }
43716 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43717             node.removeAttribute(n);
43718             
43719         }
43720         
43721         var cwhite = this.cwhite;
43722         var cblack = this.cblack;
43723             
43724         function cleanStyle(n,v)
43725         {
43726             if (v.match(/expression/)) { //XSS?? should we even bother..
43727                 node.removeAttribute(n);
43728                 return;
43729             }
43730             
43731             var parts = v.split(/;/);
43732             var clean = [];
43733             
43734             Roo.each(parts, function(p) {
43735                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43736                 if (!p.length) {
43737                     return true;
43738                 }
43739                 var l = p.split(':').shift().replace(/\s+/g,'');
43740                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43741                 
43742                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43743 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43744                     //node.removeAttribute(n);
43745                     return true;
43746                 }
43747                 //Roo.log()
43748                 // only allow 'c whitelisted system attributes'
43749                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43750 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43751                     //node.removeAttribute(n);
43752                     return true;
43753                 }
43754                 
43755                 
43756                  
43757                 
43758                 clean.push(p);
43759                 return true;
43760             });
43761             if (clean.length) { 
43762                 node.setAttribute(n, clean.join(';'));
43763             } else {
43764                 node.removeAttribute(n);
43765             }
43766             
43767         }
43768         
43769         
43770         for (var i = node.attributes.length-1; i > -1 ; i--) {
43771             var a = node.attributes[i];
43772             //console.log(a);
43773             
43774             if (a.name.toLowerCase().substr(0,2)=='on')  {
43775                 node.removeAttribute(a.name);
43776                 continue;
43777             }
43778             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43779                 node.removeAttribute(a.name);
43780                 continue;
43781             }
43782             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43783                 cleanAttr(a.name,a.value); // fixme..
43784                 continue;
43785             }
43786             if (a.name == 'style') {
43787                 cleanStyle(a.name,a.value);
43788                 continue;
43789             }
43790             /// clean up MS crap..
43791             // tecnically this should be a list of valid class'es..
43792             
43793             
43794             if (a.name == 'class') {
43795                 if (a.value.match(/^Mso/)) {
43796                     node.className = '';
43797                 }
43798                 
43799                 if (a.value.match(/body/)) {
43800                     node.className = '';
43801                 }
43802                 continue;
43803             }
43804             
43805             // style cleanup!?
43806             // class cleanup?
43807             
43808         }
43809         
43810         
43811         this.cleanUpChildren(node);
43812         
43813         
43814     },
43815     
43816     /**
43817      * Clean up MS wordisms...
43818      */
43819     cleanWord : function(node)
43820     {
43821         
43822         
43823         if (!node) {
43824             this.cleanWord(this.doc.body);
43825             return;
43826         }
43827         if (node.nodeName == "#text") {
43828             // clean up silly Windows -- stuff?
43829             return; 
43830         }
43831         if (node.nodeName == "#comment") {
43832             node.parentNode.removeChild(node);
43833             // clean up silly Windows -- stuff?
43834             return; 
43835         }
43836         
43837         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43838             node.parentNode.removeChild(node);
43839             return;
43840         }
43841         
43842         // remove - but keep children..
43843         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43844             while (node.childNodes.length) {
43845                 var cn = node.childNodes[0];
43846                 node.removeChild(cn);
43847                 node.parentNode.insertBefore(cn, node);
43848             }
43849             node.parentNode.removeChild(node);
43850             this.iterateChildren(node, this.cleanWord);
43851             return;
43852         }
43853         // clean styles
43854         if (node.className.length) {
43855             
43856             var cn = node.className.split(/\W+/);
43857             var cna = [];
43858             Roo.each(cn, function(cls) {
43859                 if (cls.match(/Mso[a-zA-Z]+/)) {
43860                     return;
43861                 }
43862                 cna.push(cls);
43863             });
43864             node.className = cna.length ? cna.join(' ') : '';
43865             if (!cna.length) {
43866                 node.removeAttribute("class");
43867             }
43868         }
43869         
43870         if (node.hasAttribute("lang")) {
43871             node.removeAttribute("lang");
43872         }
43873         
43874         if (node.hasAttribute("style")) {
43875             
43876             var styles = node.getAttribute("style").split(";");
43877             var nstyle = [];
43878             Roo.each(styles, function(s) {
43879                 if (!s.match(/:/)) {
43880                     return;
43881                 }
43882                 var kv = s.split(":");
43883                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43884                     return;
43885                 }
43886                 // what ever is left... we allow.
43887                 nstyle.push(s);
43888             });
43889             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43890             if (!nstyle.length) {
43891                 node.removeAttribute('style');
43892             }
43893         }
43894         this.iterateChildren(node, this.cleanWord);
43895         
43896         
43897         
43898     },
43899     /**
43900      * iterateChildren of a Node, calling fn each time, using this as the scole..
43901      * @param {DomNode} node node to iterate children of.
43902      * @param {Function} fn method of this class to call on each item.
43903      */
43904     iterateChildren : function(node, fn)
43905     {
43906         if (!node.childNodes.length) {
43907                 return;
43908         }
43909         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43910            fn.call(this, node.childNodes[i])
43911         }
43912     },
43913     
43914     
43915     /**
43916      * cleanTableWidths.
43917      *
43918      * Quite often pasting from word etc.. results in tables with column and widths.
43919      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43920      *
43921      */
43922     cleanTableWidths : function(node)
43923     {
43924          
43925          
43926         if (!node) {
43927             this.cleanTableWidths(this.doc.body);
43928             return;
43929         }
43930         
43931         // ignore list...
43932         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43933             return; 
43934         }
43935         Roo.log(node.tagName);
43936         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43937             this.iterateChildren(node, this.cleanTableWidths);
43938             return;
43939         }
43940         if (node.hasAttribute('width')) {
43941             node.removeAttribute('width');
43942         }
43943         
43944          
43945         if (node.hasAttribute("style")) {
43946             // pretty basic...
43947             
43948             var styles = node.getAttribute("style").split(";");
43949             var nstyle = [];
43950             Roo.each(styles, function(s) {
43951                 if (!s.match(/:/)) {
43952                     return;
43953                 }
43954                 var kv = s.split(":");
43955                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43956                     return;
43957                 }
43958                 // what ever is left... we allow.
43959                 nstyle.push(s);
43960             });
43961             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43962             if (!nstyle.length) {
43963                 node.removeAttribute('style');
43964             }
43965         }
43966         
43967         this.iterateChildren(node, this.cleanTableWidths);
43968         
43969         
43970     },
43971     
43972     
43973     
43974     
43975     domToHTML : function(currentElement, depth, nopadtext) {
43976         
43977         depth = depth || 0;
43978         nopadtext = nopadtext || false;
43979     
43980         if (!currentElement) {
43981             return this.domToHTML(this.doc.body);
43982         }
43983         
43984         //Roo.log(currentElement);
43985         var j;
43986         var allText = false;
43987         var nodeName = currentElement.nodeName;
43988         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
43989         
43990         if  (nodeName == '#text') {
43991             
43992             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
43993         }
43994         
43995         
43996         var ret = '';
43997         if (nodeName != 'BODY') {
43998              
43999             var i = 0;
44000             // Prints the node tagName, such as <A>, <IMG>, etc
44001             if (tagName) {
44002                 var attr = [];
44003                 for(i = 0; i < currentElement.attributes.length;i++) {
44004                     // quoting?
44005                     var aname = currentElement.attributes.item(i).name;
44006                     if (!currentElement.attributes.item(i).value.length) {
44007                         continue;
44008                     }
44009                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44010                 }
44011                 
44012                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44013             } 
44014             else {
44015                 
44016                 // eack
44017             }
44018         } else {
44019             tagName = false;
44020         }
44021         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44022             return ret;
44023         }
44024         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44025             nopadtext = true;
44026         }
44027         
44028         
44029         // Traverse the tree
44030         i = 0;
44031         var currentElementChild = currentElement.childNodes.item(i);
44032         var allText = true;
44033         var innerHTML  = '';
44034         lastnode = '';
44035         while (currentElementChild) {
44036             // Formatting code (indent the tree so it looks nice on the screen)
44037             var nopad = nopadtext;
44038             if (lastnode == 'SPAN') {
44039                 nopad  = true;
44040             }
44041             // text
44042             if  (currentElementChild.nodeName == '#text') {
44043                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44044                 toadd = nopadtext ? toadd : toadd.trim();
44045                 if (!nopad && toadd.length > 80) {
44046                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44047                 }
44048                 innerHTML  += toadd;
44049                 
44050                 i++;
44051                 currentElementChild = currentElement.childNodes.item(i);
44052                 lastNode = '';
44053                 continue;
44054             }
44055             allText = false;
44056             
44057             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44058                 
44059             // Recursively traverse the tree structure of the child node
44060             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44061             lastnode = currentElementChild.nodeName;
44062             i++;
44063             currentElementChild=currentElement.childNodes.item(i);
44064         }
44065         
44066         ret += innerHTML;
44067         
44068         if (!allText) {
44069                 // The remaining code is mostly for formatting the tree
44070             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44071         }
44072         
44073         
44074         if (tagName) {
44075             ret+= "</"+tagName+">";
44076         }
44077         return ret;
44078         
44079     },
44080         
44081     applyBlacklists : function()
44082     {
44083         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44084         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44085         
44086         this.white = [];
44087         this.black = [];
44088         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44089             if (b.indexOf(tag) > -1) {
44090                 return;
44091             }
44092             this.white.push(tag);
44093             
44094         }, this);
44095         
44096         Roo.each(w, function(tag) {
44097             if (b.indexOf(tag) > -1) {
44098                 return;
44099             }
44100             if (this.white.indexOf(tag) > -1) {
44101                 return;
44102             }
44103             this.white.push(tag);
44104             
44105         }, this);
44106         
44107         
44108         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44109             if (w.indexOf(tag) > -1) {
44110                 return;
44111             }
44112             this.black.push(tag);
44113             
44114         }, this);
44115         
44116         Roo.each(b, function(tag) {
44117             if (w.indexOf(tag) > -1) {
44118                 return;
44119             }
44120             if (this.black.indexOf(tag) > -1) {
44121                 return;
44122             }
44123             this.black.push(tag);
44124             
44125         }, this);
44126         
44127         
44128         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44129         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44130         
44131         this.cwhite = [];
44132         this.cblack = [];
44133         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44134             if (b.indexOf(tag) > -1) {
44135                 return;
44136             }
44137             this.cwhite.push(tag);
44138             
44139         }, this);
44140         
44141         Roo.each(w, function(tag) {
44142             if (b.indexOf(tag) > -1) {
44143                 return;
44144             }
44145             if (this.cwhite.indexOf(tag) > -1) {
44146                 return;
44147             }
44148             this.cwhite.push(tag);
44149             
44150         }, this);
44151         
44152         
44153         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44154             if (w.indexOf(tag) > -1) {
44155                 return;
44156             }
44157             this.cblack.push(tag);
44158             
44159         }, this);
44160         
44161         Roo.each(b, function(tag) {
44162             if (w.indexOf(tag) > -1) {
44163                 return;
44164             }
44165             if (this.cblack.indexOf(tag) > -1) {
44166                 return;
44167             }
44168             this.cblack.push(tag);
44169             
44170         }, this);
44171     },
44172     
44173     setStylesheets : function(stylesheets)
44174     {
44175         if(typeof(stylesheets) == 'string'){
44176             Roo.get(this.iframe.contentDocument.head).createChild({
44177                 tag : 'link',
44178                 rel : 'stylesheet',
44179                 type : 'text/css',
44180                 href : stylesheets
44181             });
44182             
44183             return;
44184         }
44185         var _this = this;
44186      
44187         Roo.each(stylesheets, function(s) {
44188             if(!s.length){
44189                 return;
44190             }
44191             
44192             Roo.get(_this.iframe.contentDocument.head).createChild({
44193                 tag : 'link',
44194                 rel : 'stylesheet',
44195                 type : 'text/css',
44196                 href : s
44197             });
44198         });
44199
44200         
44201     },
44202     
44203     removeStylesheets : function()
44204     {
44205         var _this = this;
44206         
44207         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44208             s.remove();
44209         });
44210     }
44211     
44212     // hide stuff that is not compatible
44213     /**
44214      * @event blur
44215      * @hide
44216      */
44217     /**
44218      * @event change
44219      * @hide
44220      */
44221     /**
44222      * @event focus
44223      * @hide
44224      */
44225     /**
44226      * @event specialkey
44227      * @hide
44228      */
44229     /**
44230      * @cfg {String} fieldClass @hide
44231      */
44232     /**
44233      * @cfg {String} focusClass @hide
44234      */
44235     /**
44236      * @cfg {String} autoCreate @hide
44237      */
44238     /**
44239      * @cfg {String} inputType @hide
44240      */
44241     /**
44242      * @cfg {String} invalidClass @hide
44243      */
44244     /**
44245      * @cfg {String} invalidText @hide
44246      */
44247     /**
44248      * @cfg {String} msgFx @hide
44249      */
44250     /**
44251      * @cfg {String} validateOnBlur @hide
44252      */
44253 });
44254
44255 Roo.HtmlEditorCore.white = [
44256         'area', 'br', 'img', 'input', 'hr', 'wbr',
44257         
44258        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44259        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44260        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44261        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44262        'table',   'ul',         'xmp', 
44263        
44264        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44265       'thead',   'tr', 
44266      
44267       'dir', 'menu', 'ol', 'ul', 'dl',
44268        
44269       'embed',  'object'
44270 ];
44271
44272
44273 Roo.HtmlEditorCore.black = [
44274     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44275         'applet', // 
44276         'base',   'basefont', 'bgsound', 'blink',  'body', 
44277         'frame',  'frameset', 'head',    'html',   'ilayer', 
44278         'iframe', 'layer',  'link',     'meta',    'object',   
44279         'script', 'style' ,'title',  'xml' // clean later..
44280 ];
44281 Roo.HtmlEditorCore.clean = [
44282     'script', 'style', 'title', 'xml'
44283 ];
44284 Roo.HtmlEditorCore.remove = [
44285     'font'
44286 ];
44287 // attributes..
44288
44289 Roo.HtmlEditorCore.ablack = [
44290     'on'
44291 ];
44292     
44293 Roo.HtmlEditorCore.aclean = [ 
44294     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44295 ];
44296
44297 // protocols..
44298 Roo.HtmlEditorCore.pwhite= [
44299         'http',  'https',  'mailto'
44300 ];
44301
44302 // white listed style attributes.
44303 Roo.HtmlEditorCore.cwhite= [
44304       //  'text-align', /// default is to allow most things..
44305       
44306          
44307 //        'font-size'//??
44308 ];
44309
44310 // black listed style attributes.
44311 Roo.HtmlEditorCore.cblack= [
44312       //  'font-size' -- this can be set by the project 
44313 ];
44314
44315
44316 Roo.HtmlEditorCore.swapCodes   =[ 
44317     [    8211, "--" ], 
44318     [    8212, "--" ], 
44319     [    8216,  "'" ],  
44320     [    8217, "'" ],  
44321     [    8220, '"' ],  
44322     [    8221, '"' ],  
44323     [    8226, "*" ],  
44324     [    8230, "..." ]
44325 ]; 
44326
44327     //<script type="text/javascript">
44328
44329 /*
44330  * Ext JS Library 1.1.1
44331  * Copyright(c) 2006-2007, Ext JS, LLC.
44332  * Licence LGPL
44333  * 
44334  */
44335  
44336  
44337 Roo.form.HtmlEditor = function(config){
44338     
44339     
44340     
44341     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44342     
44343     if (!this.toolbars) {
44344         this.toolbars = [];
44345     }
44346     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44347     
44348     
44349 };
44350
44351 /**
44352  * @class Roo.form.HtmlEditor
44353  * @extends Roo.form.Field
44354  * Provides a lightweight HTML Editor component.
44355  *
44356  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44357  * 
44358  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44359  * supported by this editor.</b><br/><br/>
44360  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44361  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44362  */
44363 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44364     /**
44365      * @cfg {Boolean} clearUp
44366      */
44367     clearUp : true,
44368       /**
44369      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44370      */
44371     toolbars : false,
44372    
44373      /**
44374      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44375      *                        Roo.resizable.
44376      */
44377     resizable : false,
44378      /**
44379      * @cfg {Number} height (in pixels)
44380      */   
44381     height: 300,
44382    /**
44383      * @cfg {Number} width (in pixels)
44384      */   
44385     width: 500,
44386     
44387     /**
44388      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44389      * 
44390      */
44391     stylesheets: false,
44392     
44393     
44394      /**
44395      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44396      * 
44397      */
44398     cblack: false,
44399     /**
44400      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44401      * 
44402      */
44403     cwhite: false,
44404     
44405      /**
44406      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44407      * 
44408      */
44409     black: false,
44410     /**
44411      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44412      * 
44413      */
44414     white: false,
44415     
44416     // id of frame..
44417     frameId: false,
44418     
44419     // private properties
44420     validationEvent : false,
44421     deferHeight: true,
44422     initialized : false,
44423     activated : false,
44424     
44425     onFocus : Roo.emptyFn,
44426     iframePad:3,
44427     hideMode:'offsets',
44428     
44429     actionMode : 'container', // defaults to hiding it...
44430     
44431     defaultAutoCreate : { // modified by initCompnoent..
44432         tag: "textarea",
44433         style:"width:500px;height:300px;",
44434         autocomplete: "new-password"
44435     },
44436
44437     // private
44438     initComponent : function(){
44439         this.addEvents({
44440             /**
44441              * @event initialize
44442              * Fires when the editor is fully initialized (including the iframe)
44443              * @param {HtmlEditor} this
44444              */
44445             initialize: true,
44446             /**
44447              * @event activate
44448              * Fires when the editor is first receives the focus. Any insertion must wait
44449              * until after this event.
44450              * @param {HtmlEditor} this
44451              */
44452             activate: true,
44453              /**
44454              * @event beforesync
44455              * Fires before the textarea is updated with content from the editor iframe. Return false
44456              * to cancel the sync.
44457              * @param {HtmlEditor} this
44458              * @param {String} html
44459              */
44460             beforesync: true,
44461              /**
44462              * @event beforepush
44463              * Fires before the iframe editor is updated with content from the textarea. Return false
44464              * to cancel the push.
44465              * @param {HtmlEditor} this
44466              * @param {String} html
44467              */
44468             beforepush: true,
44469              /**
44470              * @event sync
44471              * Fires when the textarea is updated with content from the editor iframe.
44472              * @param {HtmlEditor} this
44473              * @param {String} html
44474              */
44475             sync: true,
44476              /**
44477              * @event push
44478              * Fires when the iframe editor is updated with content from the textarea.
44479              * @param {HtmlEditor} this
44480              * @param {String} html
44481              */
44482             push: true,
44483              /**
44484              * @event editmodechange
44485              * Fires when the editor switches edit modes
44486              * @param {HtmlEditor} this
44487              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44488              */
44489             editmodechange: true,
44490             /**
44491              * @event editorevent
44492              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44493              * @param {HtmlEditor} this
44494              */
44495             editorevent: true,
44496             /**
44497              * @event firstfocus
44498              * Fires when on first focus - needed by toolbars..
44499              * @param {HtmlEditor} this
44500              */
44501             firstfocus: true,
44502             /**
44503              * @event autosave
44504              * Auto save the htmlEditor value as a file into Events
44505              * @param {HtmlEditor} this
44506              */
44507             autosave: true,
44508             /**
44509              * @event savedpreview
44510              * preview the saved version of htmlEditor
44511              * @param {HtmlEditor} this
44512              */
44513             savedpreview: true,
44514             
44515             /**
44516             * @event stylesheetsclick
44517             * Fires when press the Sytlesheets button
44518             * @param {Roo.HtmlEditorCore} this
44519             */
44520             stylesheetsclick: true
44521         });
44522         this.defaultAutoCreate =  {
44523             tag: "textarea",
44524             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44525             autocomplete: "new-password"
44526         };
44527     },
44528
44529     /**
44530      * Protected method that will not generally be called directly. It
44531      * is called when the editor creates its toolbar. Override this method if you need to
44532      * add custom toolbar buttons.
44533      * @param {HtmlEditor} editor
44534      */
44535     createToolbar : function(editor){
44536         Roo.log("create toolbars");
44537         if (!editor.toolbars || !editor.toolbars.length) {
44538             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44539         }
44540         
44541         for (var i =0 ; i < editor.toolbars.length;i++) {
44542             editor.toolbars[i] = Roo.factory(
44543                     typeof(editor.toolbars[i]) == 'string' ?
44544                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44545                 Roo.form.HtmlEditor);
44546             editor.toolbars[i].init(editor);
44547         }
44548          
44549         
44550     },
44551
44552      
44553     // private
44554     onRender : function(ct, position)
44555     {
44556         var _t = this;
44557         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44558         
44559         this.wrap = this.el.wrap({
44560             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44561         });
44562         
44563         this.editorcore.onRender(ct, position);
44564          
44565         if (this.resizable) {
44566             this.resizeEl = new Roo.Resizable(this.wrap, {
44567                 pinned : true,
44568                 wrap: true,
44569                 dynamic : true,
44570                 minHeight : this.height,
44571                 height: this.height,
44572                 handles : this.resizable,
44573                 width: this.width,
44574                 listeners : {
44575                     resize : function(r, w, h) {
44576                         _t.onResize(w,h); // -something
44577                     }
44578                 }
44579             });
44580             
44581         }
44582         this.createToolbar(this);
44583        
44584         
44585         if(!this.width){
44586             this.setSize(this.wrap.getSize());
44587         }
44588         if (this.resizeEl) {
44589             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44590             // should trigger onReize..
44591         }
44592         
44593         this.keyNav = new Roo.KeyNav(this.el, {
44594             
44595             "tab" : function(e){
44596                 e.preventDefault();
44597                 
44598                 var value = this.getValue();
44599                 
44600                 var start = this.el.dom.selectionStart;
44601                 var end = this.el.dom.selectionEnd;
44602                 
44603                 if(!e.shiftKey){
44604                     
44605                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44606                     this.el.dom.setSelectionRange(end + 1, end + 1);
44607                     return;
44608                 }
44609                 
44610                 var f = value.substring(0, start).split("\t");
44611                 
44612                 if(f.pop().length != 0){
44613                     return;
44614                 }
44615                 
44616                 this.setValue(f.join("\t") + value.substring(end));
44617                 this.el.dom.setSelectionRange(start - 1, start - 1);
44618                 
44619             },
44620             
44621             "home" : function(e){
44622                 e.preventDefault();
44623                 
44624                 var curr = this.el.dom.selectionStart;
44625                 var lines = this.getValue().split("\n");
44626                 
44627                 if(!lines.length){
44628                     return;
44629                 }
44630                 
44631                 if(e.ctrlKey){
44632                     this.el.dom.setSelectionRange(0, 0);
44633                     return;
44634                 }
44635                 
44636                 var pos = 0;
44637                 
44638                 for (var i = 0; i < lines.length;i++) {
44639                     pos += lines[i].length;
44640                     
44641                     if(i != 0){
44642                         pos += 1;
44643                     }
44644                     
44645                     if(pos < curr){
44646                         continue;
44647                     }
44648                     
44649                     pos -= lines[i].length;
44650                     
44651                     break;
44652                 }
44653                 
44654                 if(!e.shiftKey){
44655                     this.el.dom.setSelectionRange(pos, pos);
44656                     return;
44657                 }
44658                 
44659                 this.el.dom.selectionStart = pos;
44660                 this.el.dom.selectionEnd = curr;
44661             },
44662             
44663             "end" : function(e){
44664                 e.preventDefault();
44665                 
44666                 var curr = this.el.dom.selectionStart;
44667                 var lines = this.getValue().split("\n");
44668                 
44669                 if(!lines.length){
44670                     return;
44671                 }
44672                 
44673                 if(e.ctrlKey){
44674                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44675                     return;
44676                 }
44677                 
44678                 var pos = 0;
44679                 
44680                 for (var i = 0; i < lines.length;i++) {
44681                     
44682                     pos += lines[i].length;
44683                     
44684                     if(i != 0){
44685                         pos += 1;
44686                     }
44687                     
44688                     if(pos < curr){
44689                         continue;
44690                     }
44691                     
44692                     break;
44693                 }
44694                 
44695                 if(!e.shiftKey){
44696                     this.el.dom.setSelectionRange(pos, pos);
44697                     return;
44698                 }
44699                 
44700                 this.el.dom.selectionStart = curr;
44701                 this.el.dom.selectionEnd = pos;
44702             },
44703
44704             scope : this,
44705
44706             doRelay : function(foo, bar, hname){
44707                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44708             },
44709
44710             forceKeyDown: true
44711         });
44712         
44713 //        if(this.autosave && this.w){
44714 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44715 //        }
44716     },
44717
44718     // private
44719     onResize : function(w, h)
44720     {
44721         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44722         var ew = false;
44723         var eh = false;
44724         
44725         if(this.el ){
44726             if(typeof w == 'number'){
44727                 var aw = w - this.wrap.getFrameWidth('lr');
44728                 this.el.setWidth(this.adjustWidth('textarea', aw));
44729                 ew = aw;
44730             }
44731             if(typeof h == 'number'){
44732                 var tbh = 0;
44733                 for (var i =0; i < this.toolbars.length;i++) {
44734                     // fixme - ask toolbars for heights?
44735                     tbh += this.toolbars[i].tb.el.getHeight();
44736                     if (this.toolbars[i].footer) {
44737                         tbh += this.toolbars[i].footer.el.getHeight();
44738                     }
44739                 }
44740                 
44741                 
44742                 
44743                 
44744                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44745                 ah -= 5; // knock a few pixes off for look..
44746 //                Roo.log(ah);
44747                 this.el.setHeight(this.adjustWidth('textarea', ah));
44748                 var eh = ah;
44749             }
44750         }
44751         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44752         this.editorcore.onResize(ew,eh);
44753         
44754     },
44755
44756     /**
44757      * Toggles the editor between standard and source edit mode.
44758      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44759      */
44760     toggleSourceEdit : function(sourceEditMode)
44761     {
44762         this.editorcore.toggleSourceEdit(sourceEditMode);
44763         
44764         if(this.editorcore.sourceEditMode){
44765             Roo.log('editor - showing textarea');
44766             
44767 //            Roo.log('in');
44768 //            Roo.log(this.syncValue());
44769             this.editorcore.syncValue();
44770             this.el.removeClass('x-hidden');
44771             this.el.dom.removeAttribute('tabIndex');
44772             this.el.focus();
44773             
44774             for (var i = 0; i < this.toolbars.length; i++) {
44775                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44776                     this.toolbars[i].tb.hide();
44777                     this.toolbars[i].footer.hide();
44778                 }
44779             }
44780             
44781         }else{
44782             Roo.log('editor - hiding textarea');
44783 //            Roo.log('out')
44784 //            Roo.log(this.pushValue()); 
44785             this.editorcore.pushValue();
44786             
44787             this.el.addClass('x-hidden');
44788             this.el.dom.setAttribute('tabIndex', -1);
44789             
44790             for (var i = 0; i < this.toolbars.length; i++) {
44791                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44792                     this.toolbars[i].tb.show();
44793                     this.toolbars[i].footer.show();
44794                 }
44795             }
44796             
44797             //this.deferFocus();
44798         }
44799         
44800         this.setSize(this.wrap.getSize());
44801         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44802         
44803         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44804     },
44805  
44806     // private (for BoxComponent)
44807     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44808
44809     // private (for BoxComponent)
44810     getResizeEl : function(){
44811         return this.wrap;
44812     },
44813
44814     // private (for BoxComponent)
44815     getPositionEl : function(){
44816         return this.wrap;
44817     },
44818
44819     // private
44820     initEvents : function(){
44821         this.originalValue = this.getValue();
44822     },
44823
44824     /**
44825      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44826      * @method
44827      */
44828     markInvalid : Roo.emptyFn,
44829     /**
44830      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44831      * @method
44832      */
44833     clearInvalid : Roo.emptyFn,
44834
44835     setValue : function(v){
44836         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44837         this.editorcore.pushValue();
44838     },
44839
44840      
44841     // private
44842     deferFocus : function(){
44843         this.focus.defer(10, this);
44844     },
44845
44846     // doc'ed in Field
44847     focus : function(){
44848         this.editorcore.focus();
44849         
44850     },
44851       
44852
44853     // private
44854     onDestroy : function(){
44855         
44856         
44857         
44858         if(this.rendered){
44859             
44860             for (var i =0; i < this.toolbars.length;i++) {
44861                 // fixme - ask toolbars for heights?
44862                 this.toolbars[i].onDestroy();
44863             }
44864             
44865             this.wrap.dom.innerHTML = '';
44866             this.wrap.remove();
44867         }
44868     },
44869
44870     // private
44871     onFirstFocus : function(){
44872         //Roo.log("onFirstFocus");
44873         this.editorcore.onFirstFocus();
44874          for (var i =0; i < this.toolbars.length;i++) {
44875             this.toolbars[i].onFirstFocus();
44876         }
44877         
44878     },
44879     
44880     // private
44881     syncValue : function()
44882     {
44883         this.editorcore.syncValue();
44884     },
44885     
44886     pushValue : function()
44887     {
44888         this.editorcore.pushValue();
44889     },
44890     
44891     setStylesheets : function(stylesheets)
44892     {
44893         this.editorcore.setStylesheets(stylesheets);
44894     },
44895     
44896     removeStylesheets : function()
44897     {
44898         this.editorcore.removeStylesheets();
44899     }
44900      
44901     
44902     // hide stuff that is not compatible
44903     /**
44904      * @event blur
44905      * @hide
44906      */
44907     /**
44908      * @event change
44909      * @hide
44910      */
44911     /**
44912      * @event focus
44913      * @hide
44914      */
44915     /**
44916      * @event specialkey
44917      * @hide
44918      */
44919     /**
44920      * @cfg {String} fieldClass @hide
44921      */
44922     /**
44923      * @cfg {String} focusClass @hide
44924      */
44925     /**
44926      * @cfg {String} autoCreate @hide
44927      */
44928     /**
44929      * @cfg {String} inputType @hide
44930      */
44931     /**
44932      * @cfg {String} invalidClass @hide
44933      */
44934     /**
44935      * @cfg {String} invalidText @hide
44936      */
44937     /**
44938      * @cfg {String} msgFx @hide
44939      */
44940     /**
44941      * @cfg {String} validateOnBlur @hide
44942      */
44943 });
44944  
44945     // <script type="text/javascript">
44946 /*
44947  * Based on
44948  * Ext JS Library 1.1.1
44949  * Copyright(c) 2006-2007, Ext JS, LLC.
44950  *  
44951  
44952  */
44953
44954 /**
44955  * @class Roo.form.HtmlEditorToolbar1
44956  * Basic Toolbar
44957  * 
44958  * Usage:
44959  *
44960  new Roo.form.HtmlEditor({
44961     ....
44962     toolbars : [
44963         new Roo.form.HtmlEditorToolbar1({
44964             disable : { fonts: 1 , format: 1, ..., ... , ...],
44965             btns : [ .... ]
44966         })
44967     }
44968      
44969  * 
44970  * @cfg {Object} disable List of elements to disable..
44971  * @cfg {Array} btns List of additional buttons.
44972  * 
44973  * 
44974  * NEEDS Extra CSS? 
44975  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
44976  */
44977  
44978 Roo.form.HtmlEditor.ToolbarStandard = function(config)
44979 {
44980     
44981     Roo.apply(this, config);
44982     
44983     // default disabled, based on 'good practice'..
44984     this.disable = this.disable || {};
44985     Roo.applyIf(this.disable, {
44986         fontSize : true,
44987         colors : true,
44988         specialElements : true
44989     });
44990     
44991     
44992     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44993     // dont call parent... till later.
44994 }
44995
44996 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
44997     
44998     tb: false,
44999     
45000     rendered: false,
45001     
45002     editor : false,
45003     editorcore : false,
45004     /**
45005      * @cfg {Object} disable  List of toolbar elements to disable
45006          
45007      */
45008     disable : false,
45009     
45010     
45011      /**
45012      * @cfg {String} createLinkText The default text for the create link prompt
45013      */
45014     createLinkText : 'Please enter the URL for the link:',
45015     /**
45016      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45017      */
45018     defaultLinkValue : 'http:/'+'/',
45019    
45020     
45021       /**
45022      * @cfg {Array} fontFamilies An array of available font families
45023      */
45024     fontFamilies : [
45025         'Arial',
45026         'Courier New',
45027         'Tahoma',
45028         'Times New Roman',
45029         'Verdana'
45030     ],
45031     
45032     specialChars : [
45033            "&#169;",
45034           "&#174;",     
45035           "&#8482;",    
45036           "&#163;" ,    
45037          // "&#8212;",    
45038           "&#8230;",    
45039           "&#247;" ,    
45040         //  "&#225;" ,     ?? a acute?
45041            "&#8364;"    , //Euro
45042        //   "&#8220;"    ,
45043         //  "&#8221;"    ,
45044         //  "&#8226;"    ,
45045           "&#176;"  //   , // degrees
45046
45047          // "&#233;"     , // e ecute
45048          // "&#250;"     , // u ecute?
45049     ],
45050     
45051     specialElements : [
45052         {
45053             text: "Insert Table",
45054             xtype: 'MenuItem',
45055             xns : Roo.Menu,
45056             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45057                 
45058         },
45059         {    
45060             text: "Insert Image",
45061             xtype: 'MenuItem',
45062             xns : Roo.Menu,
45063             ihtml : '<img src="about:blank"/>'
45064             
45065         }
45066         
45067          
45068     ],
45069     
45070     
45071     inputElements : [ 
45072             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45073             "input:submit", "input:button", "select", "textarea", "label" ],
45074     formats : [
45075         ["p"] ,  
45076         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45077         ["pre"],[ "code"], 
45078         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45079         ['div'],['span']
45080     ],
45081     
45082     cleanStyles : [
45083         "font-size"
45084     ],
45085      /**
45086      * @cfg {String} defaultFont default font to use.
45087      */
45088     defaultFont: 'tahoma',
45089    
45090     fontSelect : false,
45091     
45092     
45093     formatCombo : false,
45094     
45095     init : function(editor)
45096     {
45097         this.editor = editor;
45098         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45099         var editorcore = this.editorcore;
45100         
45101         var _t = this;
45102         
45103         var fid = editorcore.frameId;
45104         var etb = this;
45105         function btn(id, toggle, handler){
45106             var xid = fid + '-'+ id ;
45107             return {
45108                 id : xid,
45109                 cmd : id,
45110                 cls : 'x-btn-icon x-edit-'+id,
45111                 enableToggle:toggle !== false,
45112                 scope: _t, // was editor...
45113                 handler:handler||_t.relayBtnCmd,
45114                 clickEvent:'mousedown',
45115                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45116                 tabIndex:-1
45117             };
45118         }
45119         
45120         
45121         
45122         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45123         this.tb = tb;
45124          // stop form submits
45125         tb.el.on('click', function(e){
45126             e.preventDefault(); // what does this do?
45127         });
45128
45129         if(!this.disable.font) { // && !Roo.isSafari){
45130             /* why no safari for fonts 
45131             editor.fontSelect = tb.el.createChild({
45132                 tag:'select',
45133                 tabIndex: -1,
45134                 cls:'x-font-select',
45135                 html: this.createFontOptions()
45136             });
45137             
45138             editor.fontSelect.on('change', function(){
45139                 var font = editor.fontSelect.dom.value;
45140                 editor.relayCmd('fontname', font);
45141                 editor.deferFocus();
45142             }, editor);
45143             
45144             tb.add(
45145                 editor.fontSelect.dom,
45146                 '-'
45147             );
45148             */
45149             
45150         };
45151         if(!this.disable.formats){
45152             this.formatCombo = new Roo.form.ComboBox({
45153                 store: new Roo.data.SimpleStore({
45154                     id : 'tag',
45155                     fields: ['tag'],
45156                     data : this.formats // from states.js
45157                 }),
45158                 blockFocus : true,
45159                 name : '',
45160                 //autoCreate : {tag: "div",  size: "20"},
45161                 displayField:'tag',
45162                 typeAhead: false,
45163                 mode: 'local',
45164                 editable : false,
45165                 triggerAction: 'all',
45166                 emptyText:'Add tag',
45167                 selectOnFocus:true,
45168                 width:135,
45169                 listeners : {
45170                     'select': function(c, r, i) {
45171                         editorcore.insertTag(r.get('tag'));
45172                         editor.focus();
45173                     }
45174                 }
45175
45176             });
45177             tb.addField(this.formatCombo);
45178             
45179         }
45180         
45181         if(!this.disable.format){
45182             tb.add(
45183                 btn('bold'),
45184                 btn('italic'),
45185                 btn('underline'),
45186                 btn('strikethrough')
45187             );
45188         };
45189         if(!this.disable.fontSize){
45190             tb.add(
45191                 '-',
45192                 
45193                 
45194                 btn('increasefontsize', false, editorcore.adjustFont),
45195                 btn('decreasefontsize', false, editorcore.adjustFont)
45196             );
45197         };
45198         
45199         
45200         if(!this.disable.colors){
45201             tb.add(
45202                 '-', {
45203                     id:editorcore.frameId +'-forecolor',
45204                     cls:'x-btn-icon x-edit-forecolor',
45205                     clickEvent:'mousedown',
45206                     tooltip: this.buttonTips['forecolor'] || undefined,
45207                     tabIndex:-1,
45208                     menu : new Roo.menu.ColorMenu({
45209                         allowReselect: true,
45210                         focus: Roo.emptyFn,
45211                         value:'000000',
45212                         plain:true,
45213                         selectHandler: function(cp, color){
45214                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45215                             editor.deferFocus();
45216                         },
45217                         scope: editorcore,
45218                         clickEvent:'mousedown'
45219                     })
45220                 }, {
45221                     id:editorcore.frameId +'backcolor',
45222                     cls:'x-btn-icon x-edit-backcolor',
45223                     clickEvent:'mousedown',
45224                     tooltip: this.buttonTips['backcolor'] || undefined,
45225                     tabIndex:-1,
45226                     menu : new Roo.menu.ColorMenu({
45227                         focus: Roo.emptyFn,
45228                         value:'FFFFFF',
45229                         plain:true,
45230                         allowReselect: true,
45231                         selectHandler: function(cp, color){
45232                             if(Roo.isGecko){
45233                                 editorcore.execCmd('useCSS', false);
45234                                 editorcore.execCmd('hilitecolor', color);
45235                                 editorcore.execCmd('useCSS', true);
45236                                 editor.deferFocus();
45237                             }else{
45238                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45239                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45240                                 editor.deferFocus();
45241                             }
45242                         },
45243                         scope:editorcore,
45244                         clickEvent:'mousedown'
45245                     })
45246                 }
45247             );
45248         };
45249         // now add all the items...
45250         
45251
45252         if(!this.disable.alignments){
45253             tb.add(
45254                 '-',
45255                 btn('justifyleft'),
45256                 btn('justifycenter'),
45257                 btn('justifyright')
45258             );
45259         };
45260
45261         //if(!Roo.isSafari){
45262             if(!this.disable.links){
45263                 tb.add(
45264                     '-',
45265                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45266                 );
45267             };
45268
45269             if(!this.disable.lists){
45270                 tb.add(
45271                     '-',
45272                     btn('insertorderedlist'),
45273                     btn('insertunorderedlist')
45274                 );
45275             }
45276             if(!this.disable.sourceEdit){
45277                 tb.add(
45278                     '-',
45279                     btn('sourceedit', true, function(btn){
45280                         this.toggleSourceEdit(btn.pressed);
45281                     })
45282                 );
45283             }
45284         //}
45285         
45286         var smenu = { };
45287         // special menu.. - needs to be tidied up..
45288         if (!this.disable.special) {
45289             smenu = {
45290                 text: "&#169;",
45291                 cls: 'x-edit-none',
45292                 
45293                 menu : {
45294                     items : []
45295                 }
45296             };
45297             for (var i =0; i < this.specialChars.length; i++) {
45298                 smenu.menu.items.push({
45299                     
45300                     html: this.specialChars[i],
45301                     handler: function(a,b) {
45302                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45303                         //editor.insertAtCursor(a.html);
45304                         
45305                     },
45306                     tabIndex:-1
45307                 });
45308             }
45309             
45310             
45311             tb.add(smenu);
45312             
45313             
45314         }
45315         
45316         var cmenu = { };
45317         if (!this.disable.cleanStyles) {
45318             cmenu = {
45319                 cls: 'x-btn-icon x-btn-clear',
45320                 
45321                 menu : {
45322                     items : []
45323                 }
45324             };
45325             for (var i =0; i < this.cleanStyles.length; i++) {
45326                 cmenu.menu.items.push({
45327                     actiontype : this.cleanStyles[i],
45328                     html: 'Remove ' + this.cleanStyles[i],
45329                     handler: function(a,b) {
45330 //                        Roo.log(a);
45331 //                        Roo.log(b);
45332                         var c = Roo.get(editorcore.doc.body);
45333                         c.select('[style]').each(function(s) {
45334                             s.dom.style.removeProperty(a.actiontype);
45335                         });
45336                         editorcore.syncValue();
45337                     },
45338                     tabIndex:-1
45339                 });
45340             }
45341              cmenu.menu.items.push({
45342                 actiontype : 'tablewidths',
45343                 html: 'Remove Table Widths',
45344                 handler: function(a,b) {
45345                     editorcore.cleanTableWidths();
45346                     editorcore.syncValue();
45347                 },
45348                 tabIndex:-1
45349             });
45350             cmenu.menu.items.push({
45351                 actiontype : 'word',
45352                 html: 'Remove MS Word Formating',
45353                 handler: function(a,b) {
45354                     editorcore.cleanWord();
45355                     editorcore.syncValue();
45356                 },
45357                 tabIndex:-1
45358             });
45359             
45360             cmenu.menu.items.push({
45361                 actiontype : 'all',
45362                 html: 'Remove All Styles',
45363                 handler: function(a,b) {
45364                     
45365                     var c = Roo.get(editorcore.doc.body);
45366                     c.select('[style]').each(function(s) {
45367                         s.dom.removeAttribute('style');
45368                     });
45369                     editorcore.syncValue();
45370                 },
45371                 tabIndex:-1
45372             });
45373             
45374             cmenu.menu.items.push({
45375                 actiontype : 'all',
45376                 html: 'Remove All CSS Classes',
45377                 handler: function(a,b) {
45378                     
45379                     var c = Roo.get(editorcore.doc.body);
45380                     c.select('[class]').each(function(s) {
45381                         s.dom.className = '';
45382                     });
45383                     editorcore.syncValue();
45384                 },
45385                 tabIndex:-1
45386             });
45387             
45388              cmenu.menu.items.push({
45389                 actiontype : 'tidy',
45390                 html: 'Tidy HTML Source',
45391                 handler: function(a,b) {
45392                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45393                     editorcore.syncValue();
45394                 },
45395                 tabIndex:-1
45396             });
45397             
45398             
45399             tb.add(cmenu);
45400         }
45401          
45402         if (!this.disable.specialElements) {
45403             var semenu = {
45404                 text: "Other;",
45405                 cls: 'x-edit-none',
45406                 menu : {
45407                     items : []
45408                 }
45409             };
45410             for (var i =0; i < this.specialElements.length; i++) {
45411                 semenu.menu.items.push(
45412                     Roo.apply({ 
45413                         handler: function(a,b) {
45414                             editor.insertAtCursor(this.ihtml);
45415                         }
45416                     }, this.specialElements[i])
45417                 );
45418                     
45419             }
45420             
45421             tb.add(semenu);
45422             
45423             
45424         }
45425          
45426         
45427         if (this.btns) {
45428             for(var i =0; i< this.btns.length;i++) {
45429                 var b = Roo.factory(this.btns[i],Roo.form);
45430                 b.cls =  'x-edit-none';
45431                 
45432                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45433                     b.cls += ' x-init-enable';
45434                 }
45435                 
45436                 b.scope = editorcore;
45437                 tb.add(b);
45438             }
45439         
45440         }
45441         
45442         
45443         
45444         // disable everything...
45445         
45446         this.tb.items.each(function(item){
45447             
45448            if(
45449                 item.id != editorcore.frameId+ '-sourceedit' && 
45450                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45451             ){
45452                 
45453                 item.disable();
45454             }
45455         });
45456         this.rendered = true;
45457         
45458         // the all the btns;
45459         editor.on('editorevent', this.updateToolbar, this);
45460         // other toolbars need to implement this..
45461         //editor.on('editmodechange', this.updateToolbar, this);
45462     },
45463     
45464     
45465     relayBtnCmd : function(btn) {
45466         this.editorcore.relayCmd(btn.cmd);
45467     },
45468     // private used internally
45469     createLink : function(){
45470         Roo.log("create link?");
45471         var url = prompt(this.createLinkText, this.defaultLinkValue);
45472         if(url && url != 'http:/'+'/'){
45473             this.editorcore.relayCmd('createlink', url);
45474         }
45475     },
45476
45477     
45478     /**
45479      * Protected method that will not generally be called directly. It triggers
45480      * a toolbar update by reading the markup state of the current selection in the editor.
45481      */
45482     updateToolbar: function(){
45483
45484         if(!this.editorcore.activated){
45485             this.editor.onFirstFocus();
45486             return;
45487         }
45488
45489         var btns = this.tb.items.map, 
45490             doc = this.editorcore.doc,
45491             frameId = this.editorcore.frameId;
45492
45493         if(!this.disable.font && !Roo.isSafari){
45494             /*
45495             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45496             if(name != this.fontSelect.dom.value){
45497                 this.fontSelect.dom.value = name;
45498             }
45499             */
45500         }
45501         if(!this.disable.format){
45502             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45503             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45504             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45505             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45506         }
45507         if(!this.disable.alignments){
45508             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45509             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45510             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45511         }
45512         if(!Roo.isSafari && !this.disable.lists){
45513             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45514             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45515         }
45516         
45517         var ans = this.editorcore.getAllAncestors();
45518         if (this.formatCombo) {
45519             
45520             
45521             var store = this.formatCombo.store;
45522             this.formatCombo.setValue("");
45523             for (var i =0; i < ans.length;i++) {
45524                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45525                     // select it..
45526                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45527                     break;
45528                 }
45529             }
45530         }
45531         
45532         
45533         
45534         // hides menus... - so this cant be on a menu...
45535         Roo.menu.MenuMgr.hideAll();
45536
45537         //this.editorsyncValue();
45538     },
45539    
45540     
45541     createFontOptions : function(){
45542         var buf = [], fs = this.fontFamilies, ff, lc;
45543         
45544         
45545         
45546         for(var i = 0, len = fs.length; i< len; i++){
45547             ff = fs[i];
45548             lc = ff.toLowerCase();
45549             buf.push(
45550                 '<option value="',lc,'" style="font-family:',ff,';"',
45551                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45552                     ff,
45553                 '</option>'
45554             );
45555         }
45556         return buf.join('');
45557     },
45558     
45559     toggleSourceEdit : function(sourceEditMode){
45560         
45561         Roo.log("toolbar toogle");
45562         if(sourceEditMode === undefined){
45563             sourceEditMode = !this.sourceEditMode;
45564         }
45565         this.sourceEditMode = sourceEditMode === true;
45566         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45567         // just toggle the button?
45568         if(btn.pressed !== this.sourceEditMode){
45569             btn.toggle(this.sourceEditMode);
45570             return;
45571         }
45572         
45573         if(sourceEditMode){
45574             Roo.log("disabling buttons");
45575             this.tb.items.each(function(item){
45576                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45577                     item.disable();
45578                 }
45579             });
45580           
45581         }else{
45582             Roo.log("enabling buttons");
45583             if(this.editorcore.initialized){
45584                 this.tb.items.each(function(item){
45585                     item.enable();
45586                 });
45587             }
45588             
45589         }
45590         Roo.log("calling toggole on editor");
45591         // tell the editor that it's been pressed..
45592         this.editor.toggleSourceEdit(sourceEditMode);
45593        
45594     },
45595      /**
45596      * Object collection of toolbar tooltips for the buttons in the editor. The key
45597      * is the command id associated with that button and the value is a valid QuickTips object.
45598      * For example:
45599 <pre><code>
45600 {
45601     bold : {
45602         title: 'Bold (Ctrl+B)',
45603         text: 'Make the selected text bold.',
45604         cls: 'x-html-editor-tip'
45605     },
45606     italic : {
45607         title: 'Italic (Ctrl+I)',
45608         text: 'Make the selected text italic.',
45609         cls: 'x-html-editor-tip'
45610     },
45611     ...
45612 </code></pre>
45613     * @type Object
45614      */
45615     buttonTips : {
45616         bold : {
45617             title: 'Bold (Ctrl+B)',
45618             text: 'Make the selected text bold.',
45619             cls: 'x-html-editor-tip'
45620         },
45621         italic : {
45622             title: 'Italic (Ctrl+I)',
45623             text: 'Make the selected text italic.',
45624             cls: 'x-html-editor-tip'
45625         },
45626         underline : {
45627             title: 'Underline (Ctrl+U)',
45628             text: 'Underline the selected text.',
45629             cls: 'x-html-editor-tip'
45630         },
45631         strikethrough : {
45632             title: 'Strikethrough',
45633             text: 'Strikethrough the selected text.',
45634             cls: 'x-html-editor-tip'
45635         },
45636         increasefontsize : {
45637             title: 'Grow Text',
45638             text: 'Increase the font size.',
45639             cls: 'x-html-editor-tip'
45640         },
45641         decreasefontsize : {
45642             title: 'Shrink Text',
45643             text: 'Decrease the font size.',
45644             cls: 'x-html-editor-tip'
45645         },
45646         backcolor : {
45647             title: 'Text Highlight Color',
45648             text: 'Change the background color of the selected text.',
45649             cls: 'x-html-editor-tip'
45650         },
45651         forecolor : {
45652             title: 'Font Color',
45653             text: 'Change the color of the selected text.',
45654             cls: 'x-html-editor-tip'
45655         },
45656         justifyleft : {
45657             title: 'Align Text Left',
45658             text: 'Align text to the left.',
45659             cls: 'x-html-editor-tip'
45660         },
45661         justifycenter : {
45662             title: 'Center Text',
45663             text: 'Center text in the editor.',
45664             cls: 'x-html-editor-tip'
45665         },
45666         justifyright : {
45667             title: 'Align Text Right',
45668             text: 'Align text to the right.',
45669             cls: 'x-html-editor-tip'
45670         },
45671         insertunorderedlist : {
45672             title: 'Bullet List',
45673             text: 'Start a bulleted list.',
45674             cls: 'x-html-editor-tip'
45675         },
45676         insertorderedlist : {
45677             title: 'Numbered List',
45678             text: 'Start a numbered list.',
45679             cls: 'x-html-editor-tip'
45680         },
45681         createlink : {
45682             title: 'Hyperlink',
45683             text: 'Make the selected text a hyperlink.',
45684             cls: 'x-html-editor-tip'
45685         },
45686         sourceedit : {
45687             title: 'Source Edit',
45688             text: 'Switch to source editing mode.',
45689             cls: 'x-html-editor-tip'
45690         }
45691     },
45692     // private
45693     onDestroy : function(){
45694         if(this.rendered){
45695             
45696             this.tb.items.each(function(item){
45697                 if(item.menu){
45698                     item.menu.removeAll();
45699                     if(item.menu.el){
45700                         item.menu.el.destroy();
45701                     }
45702                 }
45703                 item.destroy();
45704             });
45705              
45706         }
45707     },
45708     onFirstFocus: function() {
45709         this.tb.items.each(function(item){
45710            item.enable();
45711         });
45712     }
45713 });
45714
45715
45716
45717
45718 // <script type="text/javascript">
45719 /*
45720  * Based on
45721  * Ext JS Library 1.1.1
45722  * Copyright(c) 2006-2007, Ext JS, LLC.
45723  *  
45724  
45725  */
45726
45727  
45728 /**
45729  * @class Roo.form.HtmlEditor.ToolbarContext
45730  * Context Toolbar
45731  * 
45732  * Usage:
45733  *
45734  new Roo.form.HtmlEditor({
45735     ....
45736     toolbars : [
45737         { xtype: 'ToolbarStandard', styles : {} }
45738         { xtype: 'ToolbarContext', disable : {} }
45739     ]
45740 })
45741
45742      
45743  * 
45744  * @config : {Object} disable List of elements to disable.. (not done yet.)
45745  * @config : {Object} styles  Map of styles available.
45746  * 
45747  */
45748
45749 Roo.form.HtmlEditor.ToolbarContext = function(config)
45750 {
45751     
45752     Roo.apply(this, config);
45753     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45754     // dont call parent... till later.
45755     this.styles = this.styles || {};
45756 }
45757
45758  
45759
45760 Roo.form.HtmlEditor.ToolbarContext.types = {
45761     'IMG' : {
45762         width : {
45763             title: "Width",
45764             width: 40
45765         },
45766         height:  {
45767             title: "Height",
45768             width: 40
45769         },
45770         align: {
45771             title: "Align",
45772             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45773             width : 80
45774             
45775         },
45776         border: {
45777             title: "Border",
45778             width: 40
45779         },
45780         alt: {
45781             title: "Alt",
45782             width: 120
45783         },
45784         src : {
45785             title: "Src",
45786             width: 220
45787         }
45788         
45789     },
45790     'A' : {
45791         name : {
45792             title: "Name",
45793             width: 50
45794         },
45795         target:  {
45796             title: "Target",
45797             width: 120
45798         },
45799         href:  {
45800             title: "Href",
45801             width: 220
45802         } // border?
45803         
45804     },
45805     'TABLE' : {
45806         rows : {
45807             title: "Rows",
45808             width: 20
45809         },
45810         cols : {
45811             title: "Cols",
45812             width: 20
45813         },
45814         width : {
45815             title: "Width",
45816             width: 40
45817         },
45818         height : {
45819             title: "Height",
45820             width: 40
45821         },
45822         border : {
45823             title: "Border",
45824             width: 20
45825         }
45826     },
45827     'TD' : {
45828         width : {
45829             title: "Width",
45830             width: 40
45831         },
45832         height : {
45833             title: "Height",
45834             width: 40
45835         },   
45836         align: {
45837             title: "Align",
45838             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45839             width: 80
45840         },
45841         valign: {
45842             title: "Valign",
45843             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45844             width: 80
45845         },
45846         colspan: {
45847             title: "Colspan",
45848             width: 20
45849             
45850         },
45851          'font-family'  : {
45852             title : "Font",
45853             style : 'fontFamily',
45854             displayField: 'display',
45855             optname : 'font-family',
45856             width: 140
45857         }
45858     },
45859     'INPUT' : {
45860         name : {
45861             title: "name",
45862             width: 120
45863         },
45864         value : {
45865             title: "Value",
45866             width: 120
45867         },
45868         width : {
45869             title: "Width",
45870             width: 40
45871         }
45872     },
45873     'LABEL' : {
45874         'for' : {
45875             title: "For",
45876             width: 120
45877         }
45878     },
45879     'TEXTAREA' : {
45880           name : {
45881             title: "name",
45882             width: 120
45883         },
45884         rows : {
45885             title: "Rows",
45886             width: 20
45887         },
45888         cols : {
45889             title: "Cols",
45890             width: 20
45891         }
45892     },
45893     'SELECT' : {
45894         name : {
45895             title: "name",
45896             width: 120
45897         },
45898         selectoptions : {
45899             title: "Options",
45900             width: 200
45901         }
45902     },
45903     
45904     // should we really allow this??
45905     // should this just be 
45906     'BODY' : {
45907         title : {
45908             title: "Title",
45909             width: 200,
45910             disabled : true
45911         }
45912     },
45913     'SPAN' : {
45914         'font-family'  : {
45915             title : "Font",
45916             style : 'fontFamily',
45917             displayField: 'display',
45918             optname : 'font-family',
45919             width: 140
45920         }
45921     },
45922     'DIV' : {
45923         'font-family'  : {
45924             title : "Font",
45925             style : 'fontFamily',
45926             displayField: 'display',
45927             optname : 'font-family',
45928             width: 140
45929         }
45930     },
45931      'P' : {
45932         'font-family'  : {
45933             title : "Font",
45934             style : 'fontFamily',
45935             displayField: 'display',
45936             optname : 'font-family',
45937             width: 140
45938         }
45939     },
45940     
45941     '*' : {
45942         // empty..
45943     }
45944
45945 };
45946
45947 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45948 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45949
45950 Roo.form.HtmlEditor.ToolbarContext.options = {
45951         'font-family'  : [ 
45952                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45953                 [ 'Courier New', 'Courier New'],
45954                 [ 'Tahoma', 'Tahoma'],
45955                 [ 'Times New Roman,serif', 'Times'],
45956                 [ 'Verdana','Verdana' ]
45957         ]
45958 };
45959
45960 // fixme - these need to be configurable..
45961  
45962
45963 //Roo.form.HtmlEditor.ToolbarContext.types
45964
45965
45966 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
45967     
45968     tb: false,
45969     
45970     rendered: false,
45971     
45972     editor : false,
45973     editorcore : false,
45974     /**
45975      * @cfg {Object} disable  List of toolbar elements to disable
45976          
45977      */
45978     disable : false,
45979     /**
45980      * @cfg {Object} styles List of styles 
45981      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
45982      *
45983      * These must be defined in the page, so they get rendered correctly..
45984      * .headline { }
45985      * TD.underline { }
45986      * 
45987      */
45988     styles : false,
45989     
45990     options: false,
45991     
45992     toolbars : false,
45993     
45994     init : function(editor)
45995     {
45996         this.editor = editor;
45997         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45998         var editorcore = this.editorcore;
45999         
46000         var fid = editorcore.frameId;
46001         var etb = this;
46002         function btn(id, toggle, handler){
46003             var xid = fid + '-'+ id ;
46004             return {
46005                 id : xid,
46006                 cmd : id,
46007                 cls : 'x-btn-icon x-edit-'+id,
46008                 enableToggle:toggle !== false,
46009                 scope: editorcore, // was editor...
46010                 handler:handler||editorcore.relayBtnCmd,
46011                 clickEvent:'mousedown',
46012                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46013                 tabIndex:-1
46014             };
46015         }
46016         // create a new element.
46017         var wdiv = editor.wrap.createChild({
46018                 tag: 'div'
46019             }, editor.wrap.dom.firstChild.nextSibling, true);
46020         
46021         // can we do this more than once??
46022         
46023          // stop form submits
46024       
46025  
46026         // disable everything...
46027         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46028         this.toolbars = {};
46029            
46030         for (var i in  ty) {
46031           
46032             this.toolbars[i] = this.buildToolbar(ty[i],i);
46033         }
46034         this.tb = this.toolbars.BODY;
46035         this.tb.el.show();
46036         this.buildFooter();
46037         this.footer.show();
46038         editor.on('hide', function( ) { this.footer.hide() }, this);
46039         editor.on('show', function( ) { this.footer.show() }, this);
46040         
46041          
46042         this.rendered = true;
46043         
46044         // the all the btns;
46045         editor.on('editorevent', this.updateToolbar, this);
46046         // other toolbars need to implement this..
46047         //editor.on('editmodechange', this.updateToolbar, this);
46048     },
46049     
46050     
46051     
46052     /**
46053      * Protected method that will not generally be called directly. It triggers
46054      * a toolbar update by reading the markup state of the current selection in the editor.
46055      *
46056      * Note you can force an update by calling on('editorevent', scope, false)
46057      */
46058     updateToolbar: function(editor,ev,sel){
46059
46060         //Roo.log(ev);
46061         // capture mouse up - this is handy for selecting images..
46062         // perhaps should go somewhere else...
46063         if(!this.editorcore.activated){
46064              this.editor.onFirstFocus();
46065             return;
46066         }
46067         
46068         
46069         
46070         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46071         // selectNode - might want to handle IE?
46072         if (ev &&
46073             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46074             ev.target && ev.target.tagName == 'IMG') {
46075             // they have click on an image...
46076             // let's see if we can change the selection...
46077             sel = ev.target;
46078          
46079               var nodeRange = sel.ownerDocument.createRange();
46080             try {
46081                 nodeRange.selectNode(sel);
46082             } catch (e) {
46083                 nodeRange.selectNodeContents(sel);
46084             }
46085             //nodeRange.collapse(true);
46086             var s = this.editorcore.win.getSelection();
46087             s.removeAllRanges();
46088             s.addRange(nodeRange);
46089         }  
46090         
46091       
46092         var updateFooter = sel ? false : true;
46093         
46094         
46095         var ans = this.editorcore.getAllAncestors();
46096         
46097         // pick
46098         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46099         
46100         if (!sel) { 
46101             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46102             sel = sel ? sel : this.editorcore.doc.body;
46103             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46104             
46105         }
46106         // pick a menu that exists..
46107         var tn = sel.tagName.toUpperCase();
46108         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46109         
46110         tn = sel.tagName.toUpperCase();
46111         
46112         var lastSel = this.tb.selectedNode;
46113         
46114         this.tb.selectedNode = sel;
46115         
46116         // if current menu does not match..
46117         
46118         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46119                 
46120             this.tb.el.hide();
46121             ///console.log("show: " + tn);
46122             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46123             this.tb.el.show();
46124             // update name
46125             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46126             
46127             
46128             // update attributes
46129             if (this.tb.fields) {
46130                 this.tb.fields.each(function(e) {
46131                     if (e.stylename) {
46132                         e.setValue(sel.style[e.stylename]);
46133                         return;
46134                     } 
46135                    e.setValue(sel.getAttribute(e.attrname));
46136                 });
46137             }
46138             
46139             var hasStyles = false;
46140             for(var i in this.styles) {
46141                 hasStyles = true;
46142                 break;
46143             }
46144             
46145             // update styles
46146             if (hasStyles) { 
46147                 var st = this.tb.fields.item(0);
46148                 
46149                 st.store.removeAll();
46150                
46151                 
46152                 var cn = sel.className.split(/\s+/);
46153                 
46154                 var avs = [];
46155                 if (this.styles['*']) {
46156                     
46157                     Roo.each(this.styles['*'], function(v) {
46158                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46159                     });
46160                 }
46161                 if (this.styles[tn]) { 
46162                     Roo.each(this.styles[tn], function(v) {
46163                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46164                     });
46165                 }
46166                 
46167                 st.store.loadData(avs);
46168                 st.collapse();
46169                 st.setValue(cn);
46170             }
46171             // flag our selected Node.
46172             this.tb.selectedNode = sel;
46173            
46174            
46175             Roo.menu.MenuMgr.hideAll();
46176
46177         }
46178         
46179         if (!updateFooter) {
46180             //this.footDisp.dom.innerHTML = ''; 
46181             return;
46182         }
46183         // update the footer
46184         //
46185         var html = '';
46186         
46187         this.footerEls = ans.reverse();
46188         Roo.each(this.footerEls, function(a,i) {
46189             if (!a) { return; }
46190             html += html.length ? ' &gt; '  :  '';
46191             
46192             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46193             
46194         });
46195        
46196         // 
46197         var sz = this.footDisp.up('td').getSize();
46198         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46199         this.footDisp.dom.style.marginLeft = '5px';
46200         
46201         this.footDisp.dom.style.overflow = 'hidden';
46202         
46203         this.footDisp.dom.innerHTML = html;
46204             
46205         //this.editorsyncValue();
46206     },
46207      
46208     
46209    
46210        
46211     // private
46212     onDestroy : function(){
46213         if(this.rendered){
46214             
46215             this.tb.items.each(function(item){
46216                 if(item.menu){
46217                     item.menu.removeAll();
46218                     if(item.menu.el){
46219                         item.menu.el.destroy();
46220                     }
46221                 }
46222                 item.destroy();
46223             });
46224              
46225         }
46226     },
46227     onFirstFocus: function() {
46228         // need to do this for all the toolbars..
46229         this.tb.items.each(function(item){
46230            item.enable();
46231         });
46232     },
46233     buildToolbar: function(tlist, nm)
46234     {
46235         var editor = this.editor;
46236         var editorcore = this.editorcore;
46237          // create a new element.
46238         var wdiv = editor.wrap.createChild({
46239                 tag: 'div'
46240             }, editor.wrap.dom.firstChild.nextSibling, true);
46241         
46242        
46243         var tb = new Roo.Toolbar(wdiv);
46244         // add the name..
46245         
46246         tb.add(nm+ ":&nbsp;");
46247         
46248         var styles = [];
46249         for(var i in this.styles) {
46250             styles.push(i);
46251         }
46252         
46253         // styles...
46254         if (styles && styles.length) {
46255             
46256             // this needs a multi-select checkbox...
46257             tb.addField( new Roo.form.ComboBox({
46258                 store: new Roo.data.SimpleStore({
46259                     id : 'val',
46260                     fields: ['val', 'selected'],
46261                     data : [] 
46262                 }),
46263                 name : '-roo-edit-className',
46264                 attrname : 'className',
46265                 displayField: 'val',
46266                 typeAhead: false,
46267                 mode: 'local',
46268                 editable : false,
46269                 triggerAction: 'all',
46270                 emptyText:'Select Style',
46271                 selectOnFocus:true,
46272                 width: 130,
46273                 listeners : {
46274                     'select': function(c, r, i) {
46275                         // initial support only for on class per el..
46276                         tb.selectedNode.className =  r ? r.get('val') : '';
46277                         editorcore.syncValue();
46278                     }
46279                 }
46280     
46281             }));
46282         }
46283         
46284         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46285         var tbops = tbc.options;
46286         
46287         for (var i in tlist) {
46288             
46289             var item = tlist[i];
46290             tb.add(item.title + ":&nbsp;");
46291             
46292             
46293             //optname == used so you can configure the options available..
46294             var opts = item.opts ? item.opts : false;
46295             if (item.optname) {
46296                 opts = tbops[item.optname];
46297            
46298             }
46299             
46300             if (opts) {
46301                 // opts == pulldown..
46302                 tb.addField( new Roo.form.ComboBox({
46303                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46304                         id : 'val',
46305                         fields: ['val', 'display'],
46306                         data : opts  
46307                     }),
46308                     name : '-roo-edit-' + i,
46309                     attrname : i,
46310                     stylename : item.style ? item.style : false,
46311                     displayField: item.displayField ? item.displayField : 'val',
46312                     valueField :  'val',
46313                     typeAhead: false,
46314                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46315                     editable : false,
46316                     triggerAction: 'all',
46317                     emptyText:'Select',
46318                     selectOnFocus:true,
46319                     width: item.width ? item.width  : 130,
46320                     listeners : {
46321                         'select': function(c, r, i) {
46322                             if (c.stylename) {
46323                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46324                                 return;
46325                             }
46326                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46327                         }
46328                     }
46329
46330                 }));
46331                 continue;
46332                     
46333                  
46334                 
46335                 tb.addField( new Roo.form.TextField({
46336                     name: i,
46337                     width: 100,
46338                     //allowBlank:false,
46339                     value: ''
46340                 }));
46341                 continue;
46342             }
46343             tb.addField( new Roo.form.TextField({
46344                 name: '-roo-edit-' + i,
46345                 attrname : i,
46346                 
46347                 width: item.width,
46348                 //allowBlank:true,
46349                 value: '',
46350                 listeners: {
46351                     'change' : function(f, nv, ov) {
46352                         tb.selectedNode.setAttribute(f.attrname, nv);
46353                         editorcore.syncValue();
46354                     }
46355                 }
46356             }));
46357              
46358         }
46359         
46360         var _this = this;
46361         
46362         if(nm == 'BODY'){
46363             tb.addSeparator();
46364         
46365             tb.addButton( {
46366                 text: 'Stylesheets',
46367
46368                 listeners : {
46369                     click : function ()
46370                     {
46371                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46372                     }
46373                 }
46374             });
46375         }
46376         
46377         tb.addFill();
46378         tb.addButton( {
46379             text: 'Remove Tag',
46380     
46381             listeners : {
46382                 click : function ()
46383                 {
46384                     // remove
46385                     // undo does not work.
46386                      
46387                     var sn = tb.selectedNode;
46388                     
46389                     var pn = sn.parentNode;
46390                     
46391                     var stn =  sn.childNodes[0];
46392                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46393                     while (sn.childNodes.length) {
46394                         var node = sn.childNodes[0];
46395                         sn.removeChild(node);
46396                         //Roo.log(node);
46397                         pn.insertBefore(node, sn);
46398                         
46399                     }
46400                     pn.removeChild(sn);
46401                     var range = editorcore.createRange();
46402         
46403                     range.setStart(stn,0);
46404                     range.setEnd(en,0); //????
46405                     //range.selectNode(sel);
46406                     
46407                     
46408                     var selection = editorcore.getSelection();
46409                     selection.removeAllRanges();
46410                     selection.addRange(range);
46411                     
46412                     
46413                     
46414                     //_this.updateToolbar(null, null, pn);
46415                     _this.updateToolbar(null, null, null);
46416                     _this.footDisp.dom.innerHTML = ''; 
46417                 }
46418             }
46419             
46420                     
46421                 
46422             
46423         });
46424         
46425         
46426         tb.el.on('click', function(e){
46427             e.preventDefault(); // what does this do?
46428         });
46429         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46430         tb.el.hide();
46431         tb.name = nm;
46432         // dont need to disable them... as they will get hidden
46433         return tb;
46434          
46435         
46436     },
46437     buildFooter : function()
46438     {
46439         
46440         var fel = this.editor.wrap.createChild();
46441         this.footer = new Roo.Toolbar(fel);
46442         // toolbar has scrolly on left / right?
46443         var footDisp= new Roo.Toolbar.Fill();
46444         var _t = this;
46445         this.footer.add(
46446             {
46447                 text : '&lt;',
46448                 xtype: 'Button',
46449                 handler : function() {
46450                     _t.footDisp.scrollTo('left',0,true)
46451                 }
46452             }
46453         );
46454         this.footer.add( footDisp );
46455         this.footer.add( 
46456             {
46457                 text : '&gt;',
46458                 xtype: 'Button',
46459                 handler : function() {
46460                     // no animation..
46461                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46462                 }
46463             }
46464         );
46465         var fel = Roo.get(footDisp.el);
46466         fel.addClass('x-editor-context');
46467         this.footDispWrap = fel; 
46468         this.footDispWrap.overflow  = 'hidden';
46469         
46470         this.footDisp = fel.createChild();
46471         this.footDispWrap.on('click', this.onContextClick, this)
46472         
46473         
46474     },
46475     onContextClick : function (ev,dom)
46476     {
46477         ev.preventDefault();
46478         var  cn = dom.className;
46479         //Roo.log(cn);
46480         if (!cn.match(/x-ed-loc-/)) {
46481             return;
46482         }
46483         var n = cn.split('-').pop();
46484         var ans = this.footerEls;
46485         var sel = ans[n];
46486         
46487          // pick
46488         var range = this.editorcore.createRange();
46489         
46490         range.selectNodeContents(sel);
46491         //range.selectNode(sel);
46492         
46493         
46494         var selection = this.editorcore.getSelection();
46495         selection.removeAllRanges();
46496         selection.addRange(range);
46497         
46498         
46499         
46500         this.updateToolbar(null, null, sel);
46501         
46502         
46503     }
46504     
46505     
46506     
46507     
46508     
46509 });
46510
46511
46512
46513
46514
46515 /*
46516  * Based on:
46517  * Ext JS Library 1.1.1
46518  * Copyright(c) 2006-2007, Ext JS, LLC.
46519  *
46520  * Originally Released Under LGPL - original licence link has changed is not relivant.
46521  *
46522  * Fork - LGPL
46523  * <script type="text/javascript">
46524  */
46525  
46526 /**
46527  * @class Roo.form.BasicForm
46528  * @extends Roo.util.Observable
46529  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46530  * @constructor
46531  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46532  * @param {Object} config Configuration options
46533  */
46534 Roo.form.BasicForm = function(el, config){
46535     this.allItems = [];
46536     this.childForms = [];
46537     Roo.apply(this, config);
46538     /*
46539      * The Roo.form.Field items in this form.
46540      * @type MixedCollection
46541      */
46542      
46543      
46544     this.items = new Roo.util.MixedCollection(false, function(o){
46545         return o.id || (o.id = Roo.id());
46546     });
46547     this.addEvents({
46548         /**
46549          * @event beforeaction
46550          * Fires before any action is performed. Return false to cancel the action.
46551          * @param {Form} this
46552          * @param {Action} action The action to be performed
46553          */
46554         beforeaction: true,
46555         /**
46556          * @event actionfailed
46557          * Fires when an action fails.
46558          * @param {Form} this
46559          * @param {Action} action The action that failed
46560          */
46561         actionfailed : true,
46562         /**
46563          * @event actioncomplete
46564          * Fires when an action is completed.
46565          * @param {Form} this
46566          * @param {Action} action The action that completed
46567          */
46568         actioncomplete : true
46569     });
46570     if(el){
46571         this.initEl(el);
46572     }
46573     Roo.form.BasicForm.superclass.constructor.call(this);
46574 };
46575
46576 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46577     /**
46578      * @cfg {String} method
46579      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46580      */
46581     /**
46582      * @cfg {DataReader} reader
46583      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46584      * This is optional as there is built-in support for processing JSON.
46585      */
46586     /**
46587      * @cfg {DataReader} errorReader
46588      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46589      * This is completely optional as there is built-in support for processing JSON.
46590      */
46591     /**
46592      * @cfg {String} url
46593      * The URL to use for form actions if one isn't supplied in the action options.
46594      */
46595     /**
46596      * @cfg {Boolean} fileUpload
46597      * Set to true if this form is a file upload.
46598      */
46599      
46600     /**
46601      * @cfg {Object} baseParams
46602      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46603      */
46604      /**
46605      
46606     /**
46607      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46608      */
46609     timeout: 30,
46610
46611     // private
46612     activeAction : null,
46613
46614     /**
46615      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46616      * or setValues() data instead of when the form was first created.
46617      */
46618     trackResetOnLoad : false,
46619     
46620     
46621     /**
46622      * childForms - used for multi-tab forms
46623      * @type {Array}
46624      */
46625     childForms : false,
46626     
46627     /**
46628      * allItems - full list of fields.
46629      * @type {Array}
46630      */
46631     allItems : false,
46632     
46633     /**
46634      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46635      * element by passing it or its id or mask the form itself by passing in true.
46636      * @type Mixed
46637      */
46638     waitMsgTarget : false,
46639
46640     // private
46641     initEl : function(el){
46642         this.el = Roo.get(el);
46643         this.id = this.el.id || Roo.id();
46644         this.el.on('submit', this.onSubmit, this);
46645         this.el.addClass('x-form');
46646     },
46647
46648     // private
46649     onSubmit : function(e){
46650         e.stopEvent();
46651     },
46652
46653     /**
46654      * Returns true if client-side validation on the form is successful.
46655      * @return Boolean
46656      */
46657     isValid : function(){
46658         var valid = true;
46659         this.items.each(function(f){
46660            if(!f.validate()){
46661                valid = false;
46662            }
46663         });
46664         return valid;
46665     },
46666
46667     /**
46668      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46669      * @return Boolean
46670      */
46671     isDirty : function(){
46672         var dirty = false;
46673         this.items.each(function(f){
46674            if(f.isDirty()){
46675                dirty = true;
46676                return false;
46677            }
46678         });
46679         return dirty;
46680     },
46681     
46682     /**
46683      * Returns true if any fields in this form have changed since their original load. (New version)
46684      * @return Boolean
46685      */
46686     
46687     hasChanged : function()
46688     {
46689         var dirty = false;
46690         this.items.each(function(f){
46691            if(f.hasChanged()){
46692                dirty = true;
46693                return false;
46694            }
46695         });
46696         return dirty;
46697         
46698     },
46699     /**
46700      * Resets all hasChanged to 'false' -
46701      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46702      * So hasChanged storage is only to be used for this purpose
46703      * @return Boolean
46704      */
46705     resetHasChanged : function()
46706     {
46707         this.items.each(function(f){
46708            f.resetHasChanged();
46709         });
46710         
46711     },
46712     
46713     
46714     /**
46715      * Performs a predefined action (submit or load) or custom actions you define on this form.
46716      * @param {String} actionName The name of the action type
46717      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46718      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46719      * accept other config options):
46720      * <pre>
46721 Property          Type             Description
46722 ----------------  ---------------  ----------------------------------------------------------------------------------
46723 url               String           The url for the action (defaults to the form's url)
46724 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46725 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46726 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46727                                    validate the form on the client (defaults to false)
46728      * </pre>
46729      * @return {BasicForm} this
46730      */
46731     doAction : function(action, options){
46732         if(typeof action == 'string'){
46733             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46734         }
46735         if(this.fireEvent('beforeaction', this, action) !== false){
46736             this.beforeAction(action);
46737             action.run.defer(100, action);
46738         }
46739         return this;
46740     },
46741
46742     /**
46743      * Shortcut to do a submit action.
46744      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46745      * @return {BasicForm} this
46746      */
46747     submit : function(options){
46748         this.doAction('submit', options);
46749         return this;
46750     },
46751
46752     /**
46753      * Shortcut to do a load action.
46754      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46755      * @return {BasicForm} this
46756      */
46757     load : function(options){
46758         this.doAction('load', options);
46759         return this;
46760     },
46761
46762     /**
46763      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46764      * @param {Record} record The record to edit
46765      * @return {BasicForm} this
46766      */
46767     updateRecord : function(record){
46768         record.beginEdit();
46769         var fs = record.fields;
46770         fs.each(function(f){
46771             var field = this.findField(f.name);
46772             if(field){
46773                 record.set(f.name, field.getValue());
46774             }
46775         }, this);
46776         record.endEdit();
46777         return this;
46778     },
46779
46780     /**
46781      * Loads an Roo.data.Record into this form.
46782      * @param {Record} record The record to load
46783      * @return {BasicForm} this
46784      */
46785     loadRecord : function(record){
46786         this.setValues(record.data);
46787         return this;
46788     },
46789
46790     // private
46791     beforeAction : function(action){
46792         var o = action.options;
46793         
46794        
46795         if(this.waitMsgTarget === true){
46796             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46797         }else if(this.waitMsgTarget){
46798             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46799             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46800         }else {
46801             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46802         }
46803          
46804     },
46805
46806     // private
46807     afterAction : function(action, success){
46808         this.activeAction = null;
46809         var o = action.options;
46810         
46811         if(this.waitMsgTarget === true){
46812             this.el.unmask();
46813         }else if(this.waitMsgTarget){
46814             this.waitMsgTarget.unmask();
46815         }else{
46816             Roo.MessageBox.updateProgress(1);
46817             Roo.MessageBox.hide();
46818         }
46819          
46820         if(success){
46821             if(o.reset){
46822                 this.reset();
46823             }
46824             Roo.callback(o.success, o.scope, [this, action]);
46825             this.fireEvent('actioncomplete', this, action);
46826             
46827         }else{
46828             
46829             // failure condition..
46830             // we have a scenario where updates need confirming.
46831             // eg. if a locking scenario exists..
46832             // we look for { errors : { needs_confirm : true }} in the response.
46833             if (
46834                 (typeof(action.result) != 'undefined')  &&
46835                 (typeof(action.result.errors) != 'undefined')  &&
46836                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46837            ){
46838                 var _t = this;
46839                 Roo.MessageBox.confirm(
46840                     "Change requires confirmation",
46841                     action.result.errorMsg,
46842                     function(r) {
46843                         if (r != 'yes') {
46844                             return;
46845                         }
46846                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46847                     }
46848                     
46849                 );
46850                 
46851                 
46852                 
46853                 return;
46854             }
46855             
46856             Roo.callback(o.failure, o.scope, [this, action]);
46857             // show an error message if no failed handler is set..
46858             if (!this.hasListener('actionfailed')) {
46859                 Roo.MessageBox.alert("Error",
46860                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46861                         action.result.errorMsg :
46862                         "Saving Failed, please check your entries or try again"
46863                 );
46864             }
46865             
46866             this.fireEvent('actionfailed', this, action);
46867         }
46868         
46869     },
46870
46871     /**
46872      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46873      * @param {String} id The value to search for
46874      * @return Field
46875      */
46876     findField : function(id){
46877         var field = this.items.get(id);
46878         if(!field){
46879             this.items.each(function(f){
46880                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46881                     field = f;
46882                     return false;
46883                 }
46884             });
46885         }
46886         return field || null;
46887     },
46888
46889     /**
46890      * Add a secondary form to this one, 
46891      * Used to provide tabbed forms. One form is primary, with hidden values 
46892      * which mirror the elements from the other forms.
46893      * 
46894      * @param {Roo.form.Form} form to add.
46895      * 
46896      */
46897     addForm : function(form)
46898     {
46899        
46900         if (this.childForms.indexOf(form) > -1) {
46901             // already added..
46902             return;
46903         }
46904         this.childForms.push(form);
46905         var n = '';
46906         Roo.each(form.allItems, function (fe) {
46907             
46908             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46909             if (this.findField(n)) { // already added..
46910                 return;
46911             }
46912             var add = new Roo.form.Hidden({
46913                 name : n
46914             });
46915             add.render(this.el);
46916             
46917             this.add( add );
46918         }, this);
46919         
46920     },
46921     /**
46922      * Mark fields in this form invalid in bulk.
46923      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46924      * @return {BasicForm} this
46925      */
46926     markInvalid : function(errors){
46927         if(errors instanceof Array){
46928             for(var i = 0, len = errors.length; i < len; i++){
46929                 var fieldError = errors[i];
46930                 var f = this.findField(fieldError.id);
46931                 if(f){
46932                     f.markInvalid(fieldError.msg);
46933                 }
46934             }
46935         }else{
46936             var field, id;
46937             for(id in errors){
46938                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46939                     field.markInvalid(errors[id]);
46940                 }
46941             }
46942         }
46943         Roo.each(this.childForms || [], function (f) {
46944             f.markInvalid(errors);
46945         });
46946         
46947         return this;
46948     },
46949
46950     /**
46951      * Set values for fields in this form in bulk.
46952      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46953      * @return {BasicForm} this
46954      */
46955     setValues : function(values){
46956         if(values instanceof Array){ // array of objects
46957             for(var i = 0, len = values.length; i < len; i++){
46958                 var v = values[i];
46959                 var f = this.findField(v.id);
46960                 if(f){
46961                     f.setValue(v.value);
46962                     if(this.trackResetOnLoad){
46963                         f.originalValue = f.getValue();
46964                     }
46965                 }
46966             }
46967         }else{ // object hash
46968             var field, id;
46969             for(id in values){
46970                 if(typeof values[id] != 'function' && (field = this.findField(id))){
46971                     
46972                     if (field.setFromData && 
46973                         field.valueField && 
46974                         field.displayField &&
46975                         // combos' with local stores can 
46976                         // be queried via setValue()
46977                         // to set their value..
46978                         (field.store && !field.store.isLocal)
46979                         ) {
46980                         // it's a combo
46981                         var sd = { };
46982                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
46983                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
46984                         field.setFromData(sd);
46985                         
46986                     } else {
46987                         field.setValue(values[id]);
46988                     }
46989                     
46990                     
46991                     if(this.trackResetOnLoad){
46992                         field.originalValue = field.getValue();
46993                     }
46994                 }
46995             }
46996         }
46997         this.resetHasChanged();
46998         
46999         
47000         Roo.each(this.childForms || [], function (f) {
47001             f.setValues(values);
47002             f.resetHasChanged();
47003         });
47004                 
47005         return this;
47006     },
47007
47008     /**
47009      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47010      * they are returned as an array.
47011      * @param {Boolean} asString
47012      * @return {Object}
47013      */
47014     getValues : function(asString){
47015         if (this.childForms) {
47016             // copy values from the child forms
47017             Roo.each(this.childForms, function (f) {
47018                 this.setValues(f.getValues());
47019             }, this);
47020         }
47021         
47022         
47023         
47024         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47025         if(asString === true){
47026             return fs;
47027         }
47028         return Roo.urlDecode(fs);
47029     },
47030     
47031     /**
47032      * Returns the fields in this form as an object with key/value pairs. 
47033      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47034      * @return {Object}
47035      */
47036     getFieldValues : function(with_hidden)
47037     {
47038         if (this.childForms) {
47039             // copy values from the child forms
47040             // should this call getFieldValues - probably not as we do not currently copy
47041             // hidden fields when we generate..
47042             Roo.each(this.childForms, function (f) {
47043                 this.setValues(f.getValues());
47044             }, this);
47045         }
47046         
47047         var ret = {};
47048         this.items.each(function(f){
47049             if (!f.getName()) {
47050                 return;
47051             }
47052             var v = f.getValue();
47053             if (f.inputType =='radio') {
47054                 if (typeof(ret[f.getName()]) == 'undefined') {
47055                     ret[f.getName()] = ''; // empty..
47056                 }
47057                 
47058                 if (!f.el.dom.checked) {
47059                     return;
47060                     
47061                 }
47062                 v = f.el.dom.value;
47063                 
47064             }
47065             
47066             // not sure if this supported any more..
47067             if ((typeof(v) == 'object') && f.getRawValue) {
47068                 v = f.getRawValue() ; // dates..
47069             }
47070             // combo boxes where name != hiddenName...
47071             if (f.name != f.getName()) {
47072                 ret[f.name] = f.getRawValue();
47073             }
47074             ret[f.getName()] = v;
47075         });
47076         
47077         return ret;
47078     },
47079
47080     /**
47081      * Clears all invalid messages in this form.
47082      * @return {BasicForm} this
47083      */
47084     clearInvalid : function(){
47085         this.items.each(function(f){
47086            f.clearInvalid();
47087         });
47088         
47089         Roo.each(this.childForms || [], function (f) {
47090             f.clearInvalid();
47091         });
47092         
47093         
47094         return this;
47095     },
47096
47097     /**
47098      * Resets this form.
47099      * @return {BasicForm} this
47100      */
47101     reset : function(){
47102         this.items.each(function(f){
47103             f.reset();
47104         });
47105         
47106         Roo.each(this.childForms || [], function (f) {
47107             f.reset();
47108         });
47109         this.resetHasChanged();
47110         
47111         return this;
47112     },
47113
47114     /**
47115      * Add Roo.form components to this form.
47116      * @param {Field} field1
47117      * @param {Field} field2 (optional)
47118      * @param {Field} etc (optional)
47119      * @return {BasicForm} this
47120      */
47121     add : function(){
47122         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47123         return this;
47124     },
47125
47126
47127     /**
47128      * Removes a field from the items collection (does NOT remove its markup).
47129      * @param {Field} field
47130      * @return {BasicForm} this
47131      */
47132     remove : function(field){
47133         this.items.remove(field);
47134         return this;
47135     },
47136
47137     /**
47138      * Looks at the fields in this form, checks them for an id attribute,
47139      * and calls applyTo on the existing dom element with that id.
47140      * @return {BasicForm} this
47141      */
47142     render : function(){
47143         this.items.each(function(f){
47144             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47145                 f.applyTo(f.id);
47146             }
47147         });
47148         return this;
47149     },
47150
47151     /**
47152      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47153      * @param {Object} values
47154      * @return {BasicForm} this
47155      */
47156     applyToFields : function(o){
47157         this.items.each(function(f){
47158            Roo.apply(f, o);
47159         });
47160         return this;
47161     },
47162
47163     /**
47164      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47165      * @param {Object} values
47166      * @return {BasicForm} this
47167      */
47168     applyIfToFields : function(o){
47169         this.items.each(function(f){
47170            Roo.applyIf(f, o);
47171         });
47172         return this;
47173     }
47174 });
47175
47176 // back compat
47177 Roo.BasicForm = Roo.form.BasicForm;/*
47178  * Based on:
47179  * Ext JS Library 1.1.1
47180  * Copyright(c) 2006-2007, Ext JS, LLC.
47181  *
47182  * Originally Released Under LGPL - original licence link has changed is not relivant.
47183  *
47184  * Fork - LGPL
47185  * <script type="text/javascript">
47186  */
47187
47188 /**
47189  * @class Roo.form.Form
47190  * @extends Roo.form.BasicForm
47191  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47192  * @constructor
47193  * @param {Object} config Configuration options
47194  */
47195 Roo.form.Form = function(config){
47196     var xitems =  [];
47197     if (config.items) {
47198         xitems = config.items;
47199         delete config.items;
47200     }
47201    
47202     
47203     Roo.form.Form.superclass.constructor.call(this, null, config);
47204     this.url = this.url || this.action;
47205     if(!this.root){
47206         this.root = new Roo.form.Layout(Roo.applyIf({
47207             id: Roo.id()
47208         }, config));
47209     }
47210     this.active = this.root;
47211     /**
47212      * Array of all the buttons that have been added to this form via {@link addButton}
47213      * @type Array
47214      */
47215     this.buttons = [];
47216     this.allItems = [];
47217     this.addEvents({
47218         /**
47219          * @event clientvalidation
47220          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47221          * @param {Form} this
47222          * @param {Boolean} valid true if the form has passed client-side validation
47223          */
47224         clientvalidation: true,
47225         /**
47226          * @event rendered
47227          * Fires when the form is rendered
47228          * @param {Roo.form.Form} form
47229          */
47230         rendered : true
47231     });
47232     
47233     if (this.progressUrl) {
47234             // push a hidden field onto the list of fields..
47235             this.addxtype( {
47236                     xns: Roo.form, 
47237                     xtype : 'Hidden', 
47238                     name : 'UPLOAD_IDENTIFIER' 
47239             });
47240         }
47241         
47242     
47243     Roo.each(xitems, this.addxtype, this);
47244     
47245     
47246     
47247 };
47248
47249 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47250     /**
47251      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47252      */
47253     /**
47254      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47255      */
47256     /**
47257      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47258      */
47259     buttonAlign:'center',
47260
47261     /**
47262      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47263      */
47264     minButtonWidth:75,
47265
47266     /**
47267      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47268      * This property cascades to child containers if not set.
47269      */
47270     labelAlign:'left',
47271
47272     /**
47273      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47274      * fires a looping event with that state. This is required to bind buttons to the valid
47275      * state using the config value formBind:true on the button.
47276      */
47277     monitorValid : false,
47278
47279     /**
47280      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47281      */
47282     monitorPoll : 200,
47283     
47284     /**
47285      * @cfg {String} progressUrl - Url to return progress data 
47286      */
47287     
47288     progressUrl : false,
47289   
47290     /**
47291      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47292      * fields are added and the column is closed. If no fields are passed the column remains open
47293      * until end() is called.
47294      * @param {Object} config The config to pass to the column
47295      * @param {Field} field1 (optional)
47296      * @param {Field} field2 (optional)
47297      * @param {Field} etc (optional)
47298      * @return Column The column container object
47299      */
47300     column : function(c){
47301         var col = new Roo.form.Column(c);
47302         this.start(col);
47303         if(arguments.length > 1){ // duplicate code required because of Opera
47304             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47305             this.end();
47306         }
47307         return col;
47308     },
47309
47310     /**
47311      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47312      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47313      * until end() is called.
47314      * @param {Object} config The config to pass to the fieldset
47315      * @param {Field} field1 (optional)
47316      * @param {Field} field2 (optional)
47317      * @param {Field} etc (optional)
47318      * @return FieldSet The fieldset container object
47319      */
47320     fieldset : function(c){
47321         var fs = new Roo.form.FieldSet(c);
47322         this.start(fs);
47323         if(arguments.length > 1){ // duplicate code required because of Opera
47324             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47325             this.end();
47326         }
47327         return fs;
47328     },
47329
47330     /**
47331      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47332      * fields are added and the container is closed. If no fields are passed the container remains open
47333      * until end() is called.
47334      * @param {Object} config The config to pass to the Layout
47335      * @param {Field} field1 (optional)
47336      * @param {Field} field2 (optional)
47337      * @param {Field} etc (optional)
47338      * @return Layout The container object
47339      */
47340     container : function(c){
47341         var l = new Roo.form.Layout(c);
47342         this.start(l);
47343         if(arguments.length > 1){ // duplicate code required because of Opera
47344             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47345             this.end();
47346         }
47347         return l;
47348     },
47349
47350     /**
47351      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47352      * @param {Object} container A Roo.form.Layout or subclass of Layout
47353      * @return {Form} this
47354      */
47355     start : function(c){
47356         // cascade label info
47357         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47358         this.active.stack.push(c);
47359         c.ownerCt = this.active;
47360         this.active = c;
47361         return this;
47362     },
47363
47364     /**
47365      * Closes the current open container
47366      * @return {Form} this
47367      */
47368     end : function(){
47369         if(this.active == this.root){
47370             return this;
47371         }
47372         this.active = this.active.ownerCt;
47373         return this;
47374     },
47375
47376     /**
47377      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47378      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47379      * as the label of the field.
47380      * @param {Field} field1
47381      * @param {Field} field2 (optional)
47382      * @param {Field} etc. (optional)
47383      * @return {Form} this
47384      */
47385     add : function(){
47386         this.active.stack.push.apply(this.active.stack, arguments);
47387         this.allItems.push.apply(this.allItems,arguments);
47388         var r = [];
47389         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47390             if(a[i].isFormField){
47391                 r.push(a[i]);
47392             }
47393         }
47394         if(r.length > 0){
47395             Roo.form.Form.superclass.add.apply(this, r);
47396         }
47397         return this;
47398     },
47399     
47400
47401     
47402     
47403     
47404      /**
47405      * Find any element that has been added to a form, using it's ID or name
47406      * This can include framesets, columns etc. along with regular fields..
47407      * @param {String} id - id or name to find.
47408      
47409      * @return {Element} e - or false if nothing found.
47410      */
47411     findbyId : function(id)
47412     {
47413         var ret = false;
47414         if (!id) {
47415             return ret;
47416         }
47417         Roo.each(this.allItems, function(f){
47418             if (f.id == id || f.name == id ){
47419                 ret = f;
47420                 return false;
47421             }
47422         });
47423         return ret;
47424     },
47425
47426     
47427     
47428     /**
47429      * Render this form into the passed container. This should only be called once!
47430      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47431      * @return {Form} this
47432      */
47433     render : function(ct)
47434     {
47435         
47436         
47437         
47438         ct = Roo.get(ct);
47439         var o = this.autoCreate || {
47440             tag: 'form',
47441             method : this.method || 'POST',
47442             id : this.id || Roo.id()
47443         };
47444         this.initEl(ct.createChild(o));
47445
47446         this.root.render(this.el);
47447         
47448        
47449              
47450         this.items.each(function(f){
47451             f.render('x-form-el-'+f.id);
47452         });
47453
47454         if(this.buttons.length > 0){
47455             // tables are required to maintain order and for correct IE layout
47456             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47457                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47458                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47459             }}, null, true);
47460             var tr = tb.getElementsByTagName('tr')[0];
47461             for(var i = 0, len = this.buttons.length; i < len; i++) {
47462                 var b = this.buttons[i];
47463                 var td = document.createElement('td');
47464                 td.className = 'x-form-btn-td';
47465                 b.render(tr.appendChild(td));
47466             }
47467         }
47468         if(this.monitorValid){ // initialize after render
47469             this.startMonitoring();
47470         }
47471         this.fireEvent('rendered', this);
47472         return this;
47473     },
47474
47475     /**
47476      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47477      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47478      * object or a valid Roo.DomHelper element config
47479      * @param {Function} handler The function called when the button is clicked
47480      * @param {Object} scope (optional) The scope of the handler function
47481      * @return {Roo.Button}
47482      */
47483     addButton : function(config, handler, scope){
47484         var bc = {
47485             handler: handler,
47486             scope: scope,
47487             minWidth: this.minButtonWidth,
47488             hideParent:true
47489         };
47490         if(typeof config == "string"){
47491             bc.text = config;
47492         }else{
47493             Roo.apply(bc, config);
47494         }
47495         var btn = new Roo.Button(null, bc);
47496         this.buttons.push(btn);
47497         return btn;
47498     },
47499
47500      /**
47501      * Adds a series of form elements (using the xtype property as the factory method.
47502      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47503      * @param {Object} config 
47504      */
47505     
47506     addxtype : function()
47507     {
47508         var ar = Array.prototype.slice.call(arguments, 0);
47509         var ret = false;
47510         for(var i = 0; i < ar.length; i++) {
47511             if (!ar[i]) {
47512                 continue; // skip -- if this happends something invalid got sent, we 
47513                 // should ignore it, as basically that interface element will not show up
47514                 // and that should be pretty obvious!!
47515             }
47516             
47517             if (Roo.form[ar[i].xtype]) {
47518                 ar[i].form = this;
47519                 var fe = Roo.factory(ar[i], Roo.form);
47520                 if (!ret) {
47521                     ret = fe;
47522                 }
47523                 fe.form = this;
47524                 if (fe.store) {
47525                     fe.store.form = this;
47526                 }
47527                 if (fe.isLayout) {  
47528                          
47529                     this.start(fe);
47530                     this.allItems.push(fe);
47531                     if (fe.items && fe.addxtype) {
47532                         fe.addxtype.apply(fe, fe.items);
47533                         delete fe.items;
47534                     }
47535                      this.end();
47536                     continue;
47537                 }
47538                 
47539                 
47540                  
47541                 this.add(fe);
47542               //  console.log('adding ' + ar[i].xtype);
47543             }
47544             if (ar[i].xtype == 'Button') {  
47545                 //console.log('adding button');
47546                 //console.log(ar[i]);
47547                 this.addButton(ar[i]);
47548                 this.allItems.push(fe);
47549                 continue;
47550             }
47551             
47552             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47553                 alert('end is not supported on xtype any more, use items');
47554             //    this.end();
47555             //    //console.log('adding end');
47556             }
47557             
47558         }
47559         return ret;
47560     },
47561     
47562     /**
47563      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47564      * option "monitorValid"
47565      */
47566     startMonitoring : function(){
47567         if(!this.bound){
47568             this.bound = true;
47569             Roo.TaskMgr.start({
47570                 run : this.bindHandler,
47571                 interval : this.monitorPoll || 200,
47572                 scope: this
47573             });
47574         }
47575     },
47576
47577     /**
47578      * Stops monitoring of the valid state of this form
47579      */
47580     stopMonitoring : function(){
47581         this.bound = false;
47582     },
47583
47584     // private
47585     bindHandler : function(){
47586         if(!this.bound){
47587             return false; // stops binding
47588         }
47589         var valid = true;
47590         this.items.each(function(f){
47591             if(!f.isValid(true)){
47592                 valid = false;
47593                 return false;
47594             }
47595         });
47596         for(var i = 0, len = this.buttons.length; i < len; i++){
47597             var btn = this.buttons[i];
47598             if(btn.formBind === true && btn.disabled === valid){
47599                 btn.setDisabled(!valid);
47600             }
47601         }
47602         this.fireEvent('clientvalidation', this, valid);
47603     }
47604     
47605     
47606     
47607     
47608     
47609     
47610     
47611     
47612 });
47613
47614
47615 // back compat
47616 Roo.Form = Roo.form.Form;
47617 /*
47618  * Based on:
47619  * Ext JS Library 1.1.1
47620  * Copyright(c) 2006-2007, Ext JS, LLC.
47621  *
47622  * Originally Released Under LGPL - original licence link has changed is not relivant.
47623  *
47624  * Fork - LGPL
47625  * <script type="text/javascript">
47626  */
47627
47628 // as we use this in bootstrap.
47629 Roo.namespace('Roo.form');
47630  /**
47631  * @class Roo.form.Action
47632  * Internal Class used to handle form actions
47633  * @constructor
47634  * @param {Roo.form.BasicForm} el The form element or its id
47635  * @param {Object} config Configuration options
47636  */
47637
47638  
47639  
47640 // define the action interface
47641 Roo.form.Action = function(form, options){
47642     this.form = form;
47643     this.options = options || {};
47644 };
47645 /**
47646  * Client Validation Failed
47647  * @const 
47648  */
47649 Roo.form.Action.CLIENT_INVALID = 'client';
47650 /**
47651  * Server Validation Failed
47652  * @const 
47653  */
47654 Roo.form.Action.SERVER_INVALID = 'server';
47655  /**
47656  * Connect to Server Failed
47657  * @const 
47658  */
47659 Roo.form.Action.CONNECT_FAILURE = 'connect';
47660 /**
47661  * Reading Data from Server Failed
47662  * @const 
47663  */
47664 Roo.form.Action.LOAD_FAILURE = 'load';
47665
47666 Roo.form.Action.prototype = {
47667     type : 'default',
47668     failureType : undefined,
47669     response : undefined,
47670     result : undefined,
47671
47672     // interface method
47673     run : function(options){
47674
47675     },
47676
47677     // interface method
47678     success : function(response){
47679
47680     },
47681
47682     // interface method
47683     handleResponse : function(response){
47684
47685     },
47686
47687     // default connection failure
47688     failure : function(response){
47689         
47690         this.response = response;
47691         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47692         this.form.afterAction(this, false);
47693     },
47694
47695     processResponse : function(response){
47696         this.response = response;
47697         if(!response.responseText){
47698             return true;
47699         }
47700         this.result = this.handleResponse(response);
47701         return this.result;
47702     },
47703
47704     // utility functions used internally
47705     getUrl : function(appendParams){
47706         var url = this.options.url || this.form.url || this.form.el.dom.action;
47707         if(appendParams){
47708             var p = this.getParams();
47709             if(p){
47710                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47711             }
47712         }
47713         return url;
47714     },
47715
47716     getMethod : function(){
47717         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47718     },
47719
47720     getParams : function(){
47721         var bp = this.form.baseParams;
47722         var p = this.options.params;
47723         if(p){
47724             if(typeof p == "object"){
47725                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47726             }else if(typeof p == 'string' && bp){
47727                 p += '&' + Roo.urlEncode(bp);
47728             }
47729         }else if(bp){
47730             p = Roo.urlEncode(bp);
47731         }
47732         return p;
47733     },
47734
47735     createCallback : function(){
47736         return {
47737             success: this.success,
47738             failure: this.failure,
47739             scope: this,
47740             timeout: (this.form.timeout*1000),
47741             upload: this.form.fileUpload ? this.success : undefined
47742         };
47743     }
47744 };
47745
47746 Roo.form.Action.Submit = function(form, options){
47747     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47748 };
47749
47750 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47751     type : 'submit',
47752
47753     haveProgress : false,
47754     uploadComplete : false,
47755     
47756     // uploadProgress indicator.
47757     uploadProgress : function()
47758     {
47759         if (!this.form.progressUrl) {
47760             return;
47761         }
47762         
47763         if (!this.haveProgress) {
47764             Roo.MessageBox.progress("Uploading", "Uploading");
47765         }
47766         if (this.uploadComplete) {
47767            Roo.MessageBox.hide();
47768            return;
47769         }
47770         
47771         this.haveProgress = true;
47772    
47773         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47774         
47775         var c = new Roo.data.Connection();
47776         c.request({
47777             url : this.form.progressUrl,
47778             params: {
47779                 id : uid
47780             },
47781             method: 'GET',
47782             success : function(req){
47783                //console.log(data);
47784                 var rdata = false;
47785                 var edata;
47786                 try  {
47787                    rdata = Roo.decode(req.responseText)
47788                 } catch (e) {
47789                     Roo.log("Invalid data from server..");
47790                     Roo.log(edata);
47791                     return;
47792                 }
47793                 if (!rdata || !rdata.success) {
47794                     Roo.log(rdata);
47795                     Roo.MessageBox.alert(Roo.encode(rdata));
47796                     return;
47797                 }
47798                 var data = rdata.data;
47799                 
47800                 if (this.uploadComplete) {
47801                    Roo.MessageBox.hide();
47802                    return;
47803                 }
47804                    
47805                 if (data){
47806                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47807                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47808                     );
47809                 }
47810                 this.uploadProgress.defer(2000,this);
47811             },
47812        
47813             failure: function(data) {
47814                 Roo.log('progress url failed ');
47815                 Roo.log(data);
47816             },
47817             scope : this
47818         });
47819            
47820     },
47821     
47822     
47823     run : function()
47824     {
47825         // run get Values on the form, so it syncs any secondary forms.
47826         this.form.getValues();
47827         
47828         var o = this.options;
47829         var method = this.getMethod();
47830         var isPost = method == 'POST';
47831         if(o.clientValidation === false || this.form.isValid()){
47832             
47833             if (this.form.progressUrl) {
47834                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47835                     (new Date() * 1) + '' + Math.random());
47836                     
47837             } 
47838             
47839             
47840             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47841                 form:this.form.el.dom,
47842                 url:this.getUrl(!isPost),
47843                 method: method,
47844                 params:isPost ? this.getParams() : null,
47845                 isUpload: this.form.fileUpload
47846             }));
47847             
47848             this.uploadProgress();
47849
47850         }else if (o.clientValidation !== false){ // client validation failed
47851             this.failureType = Roo.form.Action.CLIENT_INVALID;
47852             this.form.afterAction(this, false);
47853         }
47854     },
47855
47856     success : function(response)
47857     {
47858         this.uploadComplete= true;
47859         if (this.haveProgress) {
47860             Roo.MessageBox.hide();
47861         }
47862         
47863         
47864         var result = this.processResponse(response);
47865         if(result === true || result.success){
47866             this.form.afterAction(this, true);
47867             return;
47868         }
47869         if(result.errors){
47870             this.form.markInvalid(result.errors);
47871             this.failureType = Roo.form.Action.SERVER_INVALID;
47872         }
47873         this.form.afterAction(this, false);
47874     },
47875     failure : function(response)
47876     {
47877         this.uploadComplete= true;
47878         if (this.haveProgress) {
47879             Roo.MessageBox.hide();
47880         }
47881         
47882         this.response = response;
47883         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47884         this.form.afterAction(this, false);
47885     },
47886     
47887     handleResponse : function(response){
47888         if(this.form.errorReader){
47889             var rs = this.form.errorReader.read(response);
47890             var errors = [];
47891             if(rs.records){
47892                 for(var i = 0, len = rs.records.length; i < len; i++) {
47893                     var r = rs.records[i];
47894                     errors[i] = r.data;
47895                 }
47896             }
47897             if(errors.length < 1){
47898                 errors = null;
47899             }
47900             return {
47901                 success : rs.success,
47902                 errors : errors
47903             };
47904         }
47905         var ret = false;
47906         try {
47907             ret = Roo.decode(response.responseText);
47908         } catch (e) {
47909             ret = {
47910                 success: false,
47911                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47912                 errors : []
47913             };
47914         }
47915         return ret;
47916         
47917     }
47918 });
47919
47920
47921 Roo.form.Action.Load = function(form, options){
47922     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47923     this.reader = this.form.reader;
47924 };
47925
47926 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47927     type : 'load',
47928
47929     run : function(){
47930         
47931         Roo.Ajax.request(Roo.apply(
47932                 this.createCallback(), {
47933                     method:this.getMethod(),
47934                     url:this.getUrl(false),
47935                     params:this.getParams()
47936         }));
47937     },
47938
47939     success : function(response){
47940         
47941         var result = this.processResponse(response);
47942         if(result === true || !result.success || !result.data){
47943             this.failureType = Roo.form.Action.LOAD_FAILURE;
47944             this.form.afterAction(this, false);
47945             return;
47946         }
47947         this.form.clearInvalid();
47948         this.form.setValues(result.data);
47949         this.form.afterAction(this, true);
47950     },
47951
47952     handleResponse : function(response){
47953         if(this.form.reader){
47954             var rs = this.form.reader.read(response);
47955             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47956             return {
47957                 success : rs.success,
47958                 data : data
47959             };
47960         }
47961         return Roo.decode(response.responseText);
47962     }
47963 });
47964
47965 Roo.form.Action.ACTION_TYPES = {
47966     'load' : Roo.form.Action.Load,
47967     'submit' : Roo.form.Action.Submit
47968 };/*
47969  * Based on:
47970  * Ext JS Library 1.1.1
47971  * Copyright(c) 2006-2007, Ext JS, LLC.
47972  *
47973  * Originally Released Under LGPL - original licence link has changed is not relivant.
47974  *
47975  * Fork - LGPL
47976  * <script type="text/javascript">
47977  */
47978  
47979 /**
47980  * @class Roo.form.Layout
47981  * @extends Roo.Component
47982  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
47983  * @constructor
47984  * @param {Object} config Configuration options
47985  */
47986 Roo.form.Layout = function(config){
47987     var xitems = [];
47988     if (config.items) {
47989         xitems = config.items;
47990         delete config.items;
47991     }
47992     Roo.form.Layout.superclass.constructor.call(this, config);
47993     this.stack = [];
47994     Roo.each(xitems, this.addxtype, this);
47995      
47996 };
47997
47998 Roo.extend(Roo.form.Layout, Roo.Component, {
47999     /**
48000      * @cfg {String/Object} autoCreate
48001      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48002      */
48003     /**
48004      * @cfg {String/Object/Function} style
48005      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48006      * a function which returns such a specification.
48007      */
48008     /**
48009      * @cfg {String} labelAlign
48010      * Valid values are "left," "top" and "right" (defaults to "left")
48011      */
48012     /**
48013      * @cfg {Number} labelWidth
48014      * Fixed width in pixels of all field labels (defaults to undefined)
48015      */
48016     /**
48017      * @cfg {Boolean} clear
48018      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48019      */
48020     clear : true,
48021     /**
48022      * @cfg {String} labelSeparator
48023      * The separator to use after field labels (defaults to ':')
48024      */
48025     labelSeparator : ':',
48026     /**
48027      * @cfg {Boolean} hideLabels
48028      * True to suppress the display of field labels in this layout (defaults to false)
48029      */
48030     hideLabels : false,
48031
48032     // private
48033     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48034     
48035     isLayout : true,
48036     
48037     // private
48038     onRender : function(ct, position){
48039         if(this.el){ // from markup
48040             this.el = Roo.get(this.el);
48041         }else {  // generate
48042             var cfg = this.getAutoCreate();
48043             this.el = ct.createChild(cfg, position);
48044         }
48045         if(this.style){
48046             this.el.applyStyles(this.style);
48047         }
48048         if(this.labelAlign){
48049             this.el.addClass('x-form-label-'+this.labelAlign);
48050         }
48051         if(this.hideLabels){
48052             this.labelStyle = "display:none";
48053             this.elementStyle = "padding-left:0;";
48054         }else{
48055             if(typeof this.labelWidth == 'number'){
48056                 this.labelStyle = "width:"+this.labelWidth+"px;";
48057                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48058             }
48059             if(this.labelAlign == 'top'){
48060                 this.labelStyle = "width:auto;";
48061                 this.elementStyle = "padding-left:0;";
48062             }
48063         }
48064         var stack = this.stack;
48065         var slen = stack.length;
48066         if(slen > 0){
48067             if(!this.fieldTpl){
48068                 var t = new Roo.Template(
48069                     '<div class="x-form-item {5}">',
48070                         '<label for="{0}" style="{2}">{1}{4}</label>',
48071                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48072                         '</div>',
48073                     '</div><div class="x-form-clear-left"></div>'
48074                 );
48075                 t.disableFormats = true;
48076                 t.compile();
48077                 Roo.form.Layout.prototype.fieldTpl = t;
48078             }
48079             for(var i = 0; i < slen; i++) {
48080                 if(stack[i].isFormField){
48081                     this.renderField(stack[i]);
48082                 }else{
48083                     this.renderComponent(stack[i]);
48084                 }
48085             }
48086         }
48087         if(this.clear){
48088             this.el.createChild({cls:'x-form-clear'});
48089         }
48090     },
48091
48092     // private
48093     renderField : function(f){
48094         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48095                f.id, //0
48096                f.fieldLabel, //1
48097                f.labelStyle||this.labelStyle||'', //2
48098                this.elementStyle||'', //3
48099                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48100                f.itemCls||this.itemCls||''  //5
48101        ], true).getPrevSibling());
48102     },
48103
48104     // private
48105     renderComponent : function(c){
48106         c.render(c.isLayout ? this.el : this.el.createChild());    
48107     },
48108     /**
48109      * Adds a object form elements (using the xtype property as the factory method.)
48110      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48111      * @param {Object} config 
48112      */
48113     addxtype : function(o)
48114     {
48115         // create the lement.
48116         o.form = this.form;
48117         var fe = Roo.factory(o, Roo.form);
48118         this.form.allItems.push(fe);
48119         this.stack.push(fe);
48120         
48121         if (fe.isFormField) {
48122             this.form.items.add(fe);
48123         }
48124          
48125         return fe;
48126     }
48127 });
48128
48129 /**
48130  * @class Roo.form.Column
48131  * @extends Roo.form.Layout
48132  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48133  * @constructor
48134  * @param {Object} config Configuration options
48135  */
48136 Roo.form.Column = function(config){
48137     Roo.form.Column.superclass.constructor.call(this, config);
48138 };
48139
48140 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48141     /**
48142      * @cfg {Number/String} width
48143      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48144      */
48145     /**
48146      * @cfg {String/Object} autoCreate
48147      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48148      */
48149
48150     // private
48151     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48152
48153     // private
48154     onRender : function(ct, position){
48155         Roo.form.Column.superclass.onRender.call(this, ct, position);
48156         if(this.width){
48157             this.el.setWidth(this.width);
48158         }
48159     }
48160 });
48161
48162
48163 /**
48164  * @class Roo.form.Row
48165  * @extends Roo.form.Layout
48166  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48167  * @constructor
48168  * @param {Object} config Configuration options
48169  */
48170
48171  
48172 Roo.form.Row = function(config){
48173     Roo.form.Row.superclass.constructor.call(this, config);
48174 };
48175  
48176 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48177       /**
48178      * @cfg {Number/String} width
48179      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48180      */
48181     /**
48182      * @cfg {Number/String} height
48183      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48184      */
48185     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48186     
48187     padWidth : 20,
48188     // private
48189     onRender : function(ct, position){
48190         //console.log('row render');
48191         if(!this.rowTpl){
48192             var t = new Roo.Template(
48193                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48194                     '<label for="{0}" style="{2}">{1}{4}</label>',
48195                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48196                     '</div>',
48197                 '</div>'
48198             );
48199             t.disableFormats = true;
48200             t.compile();
48201             Roo.form.Layout.prototype.rowTpl = t;
48202         }
48203         this.fieldTpl = this.rowTpl;
48204         
48205         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48206         var labelWidth = 100;
48207         
48208         if ((this.labelAlign != 'top')) {
48209             if (typeof this.labelWidth == 'number') {
48210                 labelWidth = this.labelWidth
48211             }
48212             this.padWidth =  20 + labelWidth;
48213             
48214         }
48215         
48216         Roo.form.Column.superclass.onRender.call(this, ct, position);
48217         if(this.width){
48218             this.el.setWidth(this.width);
48219         }
48220         if(this.height){
48221             this.el.setHeight(this.height);
48222         }
48223     },
48224     
48225     // private
48226     renderField : function(f){
48227         f.fieldEl = this.fieldTpl.append(this.el, [
48228                f.id, f.fieldLabel,
48229                f.labelStyle||this.labelStyle||'',
48230                this.elementStyle||'',
48231                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48232                f.itemCls||this.itemCls||'',
48233                f.width ? f.width + this.padWidth : 160 + this.padWidth
48234        ],true);
48235     }
48236 });
48237  
48238
48239 /**
48240  * @class Roo.form.FieldSet
48241  * @extends Roo.form.Layout
48242  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48243  * @constructor
48244  * @param {Object} config Configuration options
48245  */
48246 Roo.form.FieldSet = function(config){
48247     Roo.form.FieldSet.superclass.constructor.call(this, config);
48248 };
48249
48250 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48251     /**
48252      * @cfg {String} legend
48253      * The text to display as the legend for the FieldSet (defaults to '')
48254      */
48255     /**
48256      * @cfg {String/Object} autoCreate
48257      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48258      */
48259
48260     // private
48261     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48262
48263     // private
48264     onRender : function(ct, position){
48265         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48266         if(this.legend){
48267             this.setLegend(this.legend);
48268         }
48269     },
48270
48271     // private
48272     setLegend : function(text){
48273         if(this.rendered){
48274             this.el.child('legend').update(text);
48275         }
48276     }
48277 });/*
48278  * Based on:
48279  * Ext JS Library 1.1.1
48280  * Copyright(c) 2006-2007, Ext JS, LLC.
48281  *
48282  * Originally Released Under LGPL - original licence link has changed is not relivant.
48283  *
48284  * Fork - LGPL
48285  * <script type="text/javascript">
48286  */
48287 /**
48288  * @class Roo.form.VTypes
48289  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48290  * @singleton
48291  */
48292 Roo.form.VTypes = function(){
48293     // closure these in so they are only created once.
48294     var alpha = /^[a-zA-Z_]+$/;
48295     var alphanum = /^[a-zA-Z0-9_]+$/;
48296     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48297     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48298
48299     // All these messages and functions are configurable
48300     return {
48301         /**
48302          * The function used to validate email addresses
48303          * @param {String} value The email address
48304          */
48305         'email' : function(v){
48306             return email.test(v);
48307         },
48308         /**
48309          * The error text to display when the email validation function returns false
48310          * @type String
48311          */
48312         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48313         /**
48314          * The keystroke filter mask to be applied on email input
48315          * @type RegExp
48316          */
48317         'emailMask' : /[a-z0-9_\.\-@]/i,
48318
48319         /**
48320          * The function used to validate URLs
48321          * @param {String} value The URL
48322          */
48323         'url' : function(v){
48324             return url.test(v);
48325         },
48326         /**
48327          * The error text to display when the url validation function returns false
48328          * @type String
48329          */
48330         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48331         
48332         /**
48333          * The function used to validate alpha values
48334          * @param {String} value The value
48335          */
48336         'alpha' : function(v){
48337             return alpha.test(v);
48338         },
48339         /**
48340          * The error text to display when the alpha validation function returns false
48341          * @type String
48342          */
48343         'alphaText' : 'This field should only contain letters and _',
48344         /**
48345          * The keystroke filter mask to be applied on alpha input
48346          * @type RegExp
48347          */
48348         'alphaMask' : /[a-z_]/i,
48349
48350         /**
48351          * The function used to validate alphanumeric values
48352          * @param {String} value The value
48353          */
48354         'alphanum' : function(v){
48355             return alphanum.test(v);
48356         },
48357         /**
48358          * The error text to display when the alphanumeric validation function returns false
48359          * @type String
48360          */
48361         'alphanumText' : 'This field should only contain letters, numbers and _',
48362         /**
48363          * The keystroke filter mask to be applied on alphanumeric input
48364          * @type RegExp
48365          */
48366         'alphanumMask' : /[a-z0-9_]/i
48367     };
48368 }();//<script type="text/javascript">
48369
48370 /**
48371  * @class Roo.form.FCKeditor
48372  * @extends Roo.form.TextArea
48373  * Wrapper around the FCKEditor http://www.fckeditor.net
48374  * @constructor
48375  * Creates a new FCKeditor
48376  * @param {Object} config Configuration options
48377  */
48378 Roo.form.FCKeditor = function(config){
48379     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48380     this.addEvents({
48381          /**
48382          * @event editorinit
48383          * Fired when the editor is initialized - you can add extra handlers here..
48384          * @param {FCKeditor} this
48385          * @param {Object} the FCK object.
48386          */
48387         editorinit : true
48388     });
48389     
48390     
48391 };
48392 Roo.form.FCKeditor.editors = { };
48393 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48394 {
48395     //defaultAutoCreate : {
48396     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48397     //},
48398     // private
48399     /**
48400      * @cfg {Object} fck options - see fck manual for details.
48401      */
48402     fckconfig : false,
48403     
48404     /**
48405      * @cfg {Object} fck toolbar set (Basic or Default)
48406      */
48407     toolbarSet : 'Basic',
48408     /**
48409      * @cfg {Object} fck BasePath
48410      */ 
48411     basePath : '/fckeditor/',
48412     
48413     
48414     frame : false,
48415     
48416     value : '',
48417     
48418    
48419     onRender : function(ct, position)
48420     {
48421         if(!this.el){
48422             this.defaultAutoCreate = {
48423                 tag: "textarea",
48424                 style:"width:300px;height:60px;",
48425                 autocomplete: "new-password"
48426             };
48427         }
48428         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48429         /*
48430         if(this.grow){
48431             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48432             if(this.preventScrollbars){
48433                 this.el.setStyle("overflow", "hidden");
48434             }
48435             this.el.setHeight(this.growMin);
48436         }
48437         */
48438         //console.log('onrender' + this.getId() );
48439         Roo.form.FCKeditor.editors[this.getId()] = this;
48440          
48441
48442         this.replaceTextarea() ;
48443         
48444     },
48445     
48446     getEditor : function() {
48447         return this.fckEditor;
48448     },
48449     /**
48450      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48451      * @param {Mixed} value The value to set
48452      */
48453     
48454     
48455     setValue : function(value)
48456     {
48457         //console.log('setValue: ' + value);
48458         
48459         if(typeof(value) == 'undefined') { // not sure why this is happending...
48460             return;
48461         }
48462         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48463         
48464         //if(!this.el || !this.getEditor()) {
48465         //    this.value = value;
48466             //this.setValue.defer(100,this,[value]);    
48467         //    return;
48468         //} 
48469         
48470         if(!this.getEditor()) {
48471             return;
48472         }
48473         
48474         this.getEditor().SetData(value);
48475         
48476         //
48477
48478     },
48479
48480     /**
48481      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48482      * @return {Mixed} value The field value
48483      */
48484     getValue : function()
48485     {
48486         
48487         if (this.frame && this.frame.dom.style.display == 'none') {
48488             return Roo.form.FCKeditor.superclass.getValue.call(this);
48489         }
48490         
48491         if(!this.el || !this.getEditor()) {
48492            
48493            // this.getValue.defer(100,this); 
48494             return this.value;
48495         }
48496        
48497         
48498         var value=this.getEditor().GetData();
48499         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48500         return Roo.form.FCKeditor.superclass.getValue.call(this);
48501         
48502
48503     },
48504
48505     /**
48506      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48507      * @return {Mixed} value The field value
48508      */
48509     getRawValue : function()
48510     {
48511         if (this.frame && this.frame.dom.style.display == 'none') {
48512             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48513         }
48514         
48515         if(!this.el || !this.getEditor()) {
48516             //this.getRawValue.defer(100,this); 
48517             return this.value;
48518             return;
48519         }
48520         
48521         
48522         
48523         var value=this.getEditor().GetData();
48524         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48525         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48526          
48527     },
48528     
48529     setSize : function(w,h) {
48530         
48531         
48532         
48533         //if (this.frame && this.frame.dom.style.display == 'none') {
48534         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48535         //    return;
48536         //}
48537         //if(!this.el || !this.getEditor()) {
48538         //    this.setSize.defer(100,this, [w,h]); 
48539         //    return;
48540         //}
48541         
48542         
48543         
48544         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48545         
48546         this.frame.dom.setAttribute('width', w);
48547         this.frame.dom.setAttribute('height', h);
48548         this.frame.setSize(w,h);
48549         
48550     },
48551     
48552     toggleSourceEdit : function(value) {
48553         
48554       
48555          
48556         this.el.dom.style.display = value ? '' : 'none';
48557         this.frame.dom.style.display = value ?  'none' : '';
48558         
48559     },
48560     
48561     
48562     focus: function(tag)
48563     {
48564         if (this.frame.dom.style.display == 'none') {
48565             return Roo.form.FCKeditor.superclass.focus.call(this);
48566         }
48567         if(!this.el || !this.getEditor()) {
48568             this.focus.defer(100,this, [tag]); 
48569             return;
48570         }
48571         
48572         
48573         
48574         
48575         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48576         this.getEditor().Focus();
48577         if (tgs.length) {
48578             if (!this.getEditor().Selection.GetSelection()) {
48579                 this.focus.defer(100,this, [tag]); 
48580                 return;
48581             }
48582             
48583             
48584             var r = this.getEditor().EditorDocument.createRange();
48585             r.setStart(tgs[0],0);
48586             r.setEnd(tgs[0],0);
48587             this.getEditor().Selection.GetSelection().removeAllRanges();
48588             this.getEditor().Selection.GetSelection().addRange(r);
48589             this.getEditor().Focus();
48590         }
48591         
48592     },
48593     
48594     
48595     
48596     replaceTextarea : function()
48597     {
48598         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48599             return ;
48600         }
48601         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48602         //{
48603             // We must check the elements firstly using the Id and then the name.
48604         var oTextarea = document.getElementById( this.getId() );
48605         
48606         var colElementsByName = document.getElementsByName( this.getId() ) ;
48607          
48608         oTextarea.style.display = 'none' ;
48609
48610         if ( oTextarea.tabIndex ) {            
48611             this.TabIndex = oTextarea.tabIndex ;
48612         }
48613         
48614         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48615         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48616         this.frame = Roo.get(this.getId() + '___Frame')
48617     },
48618     
48619     _getConfigHtml : function()
48620     {
48621         var sConfig = '' ;
48622
48623         for ( var o in this.fckconfig ) {
48624             sConfig += sConfig.length > 0  ? '&amp;' : '';
48625             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48626         }
48627
48628         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48629     },
48630     
48631     
48632     _getIFrameHtml : function()
48633     {
48634         var sFile = 'fckeditor.html' ;
48635         /* no idea what this is about..
48636         try
48637         {
48638             if ( (/fcksource=true/i).test( window.top.location.search ) )
48639                 sFile = 'fckeditor.original.html' ;
48640         }
48641         catch (e) { 
48642         */
48643
48644         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48645         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48646         
48647         
48648         var html = '<iframe id="' + this.getId() +
48649             '___Frame" src="' + sLink +
48650             '" width="' + this.width +
48651             '" height="' + this.height + '"' +
48652             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48653             ' frameborder="0" scrolling="no"></iframe>' ;
48654
48655         return html ;
48656     },
48657     
48658     _insertHtmlBefore : function( html, element )
48659     {
48660         if ( element.insertAdjacentHTML )       {
48661             // IE
48662             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48663         } else { // Gecko
48664             var oRange = document.createRange() ;
48665             oRange.setStartBefore( element ) ;
48666             var oFragment = oRange.createContextualFragment( html );
48667             element.parentNode.insertBefore( oFragment, element ) ;
48668         }
48669     }
48670     
48671     
48672   
48673     
48674     
48675     
48676     
48677
48678 });
48679
48680 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48681
48682 function FCKeditor_OnComplete(editorInstance){
48683     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48684     f.fckEditor = editorInstance;
48685     //console.log("loaded");
48686     f.fireEvent('editorinit', f, editorInstance);
48687
48688   
48689
48690  
48691
48692
48693
48694
48695
48696
48697
48698
48699
48700
48701
48702
48703
48704
48705
48706 //<script type="text/javascript">
48707 /**
48708  * @class Roo.form.GridField
48709  * @extends Roo.form.Field
48710  * Embed a grid (or editable grid into a form)
48711  * STATUS ALPHA
48712  * 
48713  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48714  * it needs 
48715  * xgrid.store = Roo.data.Store
48716  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48717  * xgrid.store.reader = Roo.data.JsonReader 
48718  * 
48719  * 
48720  * @constructor
48721  * Creates a new GridField
48722  * @param {Object} config Configuration options
48723  */
48724 Roo.form.GridField = function(config){
48725     Roo.form.GridField.superclass.constructor.call(this, config);
48726      
48727 };
48728
48729 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48730     /**
48731      * @cfg {Number} width  - used to restrict width of grid..
48732      */
48733     width : 100,
48734     /**
48735      * @cfg {Number} height - used to restrict height of grid..
48736      */
48737     height : 50,
48738      /**
48739      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48740          * 
48741          *}
48742      */
48743     xgrid : false, 
48744     /**
48745      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48746      * {tag: "input", type: "checkbox", autocomplete: "off"})
48747      */
48748    // defaultAutoCreate : { tag: 'div' },
48749     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48750     /**
48751      * @cfg {String} addTitle Text to include for adding a title.
48752      */
48753     addTitle : false,
48754     //
48755     onResize : function(){
48756         Roo.form.Field.superclass.onResize.apply(this, arguments);
48757     },
48758
48759     initEvents : function(){
48760         // Roo.form.Checkbox.superclass.initEvents.call(this);
48761         // has no events...
48762        
48763     },
48764
48765
48766     getResizeEl : function(){
48767         return this.wrap;
48768     },
48769
48770     getPositionEl : function(){
48771         return this.wrap;
48772     },
48773
48774     // private
48775     onRender : function(ct, position){
48776         
48777         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48778         var style = this.style;
48779         delete this.style;
48780         
48781         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48782         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48783         this.viewEl = this.wrap.createChild({ tag: 'div' });
48784         if (style) {
48785             this.viewEl.applyStyles(style);
48786         }
48787         if (this.width) {
48788             this.viewEl.setWidth(this.width);
48789         }
48790         if (this.height) {
48791             this.viewEl.setHeight(this.height);
48792         }
48793         //if(this.inputValue !== undefined){
48794         //this.setValue(this.value);
48795         
48796         
48797         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48798         
48799         
48800         this.grid.render();
48801         this.grid.getDataSource().on('remove', this.refreshValue, this);
48802         this.grid.getDataSource().on('update', this.refreshValue, this);
48803         this.grid.on('afteredit', this.refreshValue, this);
48804  
48805     },
48806      
48807     
48808     /**
48809      * Sets the value of the item. 
48810      * @param {String} either an object  or a string..
48811      */
48812     setValue : function(v){
48813         //this.value = v;
48814         v = v || []; // empty set..
48815         // this does not seem smart - it really only affects memoryproxy grids..
48816         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48817             var ds = this.grid.getDataSource();
48818             // assumes a json reader..
48819             var data = {}
48820             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48821             ds.loadData( data);
48822         }
48823         // clear selection so it does not get stale.
48824         if (this.grid.sm) { 
48825             this.grid.sm.clearSelections();
48826         }
48827         
48828         Roo.form.GridField.superclass.setValue.call(this, v);
48829         this.refreshValue();
48830         // should load data in the grid really....
48831     },
48832     
48833     // private
48834     refreshValue: function() {
48835          var val = [];
48836         this.grid.getDataSource().each(function(r) {
48837             val.push(r.data);
48838         });
48839         this.el.dom.value = Roo.encode(val);
48840     }
48841     
48842      
48843     
48844     
48845 });/*
48846  * Based on:
48847  * Ext JS Library 1.1.1
48848  * Copyright(c) 2006-2007, Ext JS, LLC.
48849  *
48850  * Originally Released Under LGPL - original licence link has changed is not relivant.
48851  *
48852  * Fork - LGPL
48853  * <script type="text/javascript">
48854  */
48855 /**
48856  * @class Roo.form.DisplayField
48857  * @extends Roo.form.Field
48858  * A generic Field to display non-editable data.
48859  * @cfg {Boolean} closable (true|false) default false
48860  * @constructor
48861  * Creates a new Display Field item.
48862  * @param {Object} config Configuration options
48863  */
48864 Roo.form.DisplayField = function(config){
48865     Roo.form.DisplayField.superclass.constructor.call(this, config);
48866     
48867     this.addEvents({
48868         /**
48869          * @event close
48870          * Fires after the click the close btn
48871              * @param {Roo.form.DisplayField} this
48872              */
48873         close : true
48874     });
48875 };
48876
48877 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48878     inputType:      'hidden',
48879     allowBlank:     true,
48880     readOnly:         true,
48881     
48882  
48883     /**
48884      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48885      */
48886     focusClass : undefined,
48887     /**
48888      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48889      */
48890     fieldClass: 'x-form-field',
48891     
48892      /**
48893      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48894      */
48895     valueRenderer: undefined,
48896     
48897     width: 100,
48898     /**
48899      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48900      * {tag: "input", type: "checkbox", autocomplete: "off"})
48901      */
48902      
48903  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48904  
48905     closable : false,
48906     
48907     onResize : function(){
48908         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48909         
48910     },
48911
48912     initEvents : function(){
48913         // Roo.form.Checkbox.superclass.initEvents.call(this);
48914         // has no events...
48915         
48916         if(this.closable){
48917             this.closeEl.on('click', this.onClose, this);
48918         }
48919        
48920     },
48921
48922
48923     getResizeEl : function(){
48924         return this.wrap;
48925     },
48926
48927     getPositionEl : function(){
48928         return this.wrap;
48929     },
48930
48931     // private
48932     onRender : function(ct, position){
48933         
48934         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48935         //if(this.inputValue !== undefined){
48936         this.wrap = this.el.wrap();
48937         
48938         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48939         
48940         if(this.closable){
48941             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48942         }
48943         
48944         if (this.bodyStyle) {
48945             this.viewEl.applyStyles(this.bodyStyle);
48946         }
48947         //this.viewEl.setStyle('padding', '2px');
48948         
48949         this.setValue(this.value);
48950         
48951     },
48952 /*
48953     // private
48954     initValue : Roo.emptyFn,
48955
48956   */
48957
48958         // private
48959     onClick : function(){
48960         
48961     },
48962
48963     /**
48964      * Sets the checked state of the checkbox.
48965      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
48966      */
48967     setValue : function(v){
48968         this.value = v;
48969         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
48970         // this might be called before we have a dom element..
48971         if (!this.viewEl) {
48972             return;
48973         }
48974         this.viewEl.dom.innerHTML = html;
48975         Roo.form.DisplayField.superclass.setValue.call(this, v);
48976
48977     },
48978     
48979     onClose : function(e)
48980     {
48981         e.preventDefault();
48982         
48983         this.fireEvent('close', this);
48984     }
48985 });/*
48986  * 
48987  * Licence- LGPL
48988  * 
48989  */
48990
48991 /**
48992  * @class Roo.form.DayPicker
48993  * @extends Roo.form.Field
48994  * A Day picker show [M] [T] [W] ....
48995  * @constructor
48996  * Creates a new Day Picker
48997  * @param {Object} config Configuration options
48998  */
48999 Roo.form.DayPicker= function(config){
49000     Roo.form.DayPicker.superclass.constructor.call(this, config);
49001      
49002 };
49003
49004 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49005     /**
49006      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49007      */
49008     focusClass : undefined,
49009     /**
49010      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49011      */
49012     fieldClass: "x-form-field",
49013    
49014     /**
49015      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49016      * {tag: "input", type: "checkbox", autocomplete: "off"})
49017      */
49018     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49019     
49020    
49021     actionMode : 'viewEl', 
49022     //
49023     // private
49024  
49025     inputType : 'hidden',
49026     
49027      
49028     inputElement: false, // real input element?
49029     basedOn: false, // ????
49030     
49031     isFormField: true, // not sure where this is needed!!!!
49032
49033     onResize : function(){
49034         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49035         if(!this.boxLabel){
49036             this.el.alignTo(this.wrap, 'c-c');
49037         }
49038     },
49039
49040     initEvents : function(){
49041         Roo.form.Checkbox.superclass.initEvents.call(this);
49042         this.el.on("click", this.onClick,  this);
49043         this.el.on("change", this.onClick,  this);
49044     },
49045
49046
49047     getResizeEl : function(){
49048         return this.wrap;
49049     },
49050
49051     getPositionEl : function(){
49052         return this.wrap;
49053     },
49054
49055     
49056     // private
49057     onRender : function(ct, position){
49058         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49059        
49060         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49061         
49062         var r1 = '<table><tr>';
49063         var r2 = '<tr class="x-form-daypick-icons">';
49064         for (var i=0; i < 7; i++) {
49065             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49066             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49067         }
49068         
49069         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49070         viewEl.select('img').on('click', this.onClick, this);
49071         this.viewEl = viewEl;   
49072         
49073         
49074         // this will not work on Chrome!!!
49075         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49076         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49077         
49078         
49079           
49080
49081     },
49082
49083     // private
49084     initValue : Roo.emptyFn,
49085
49086     /**
49087      * Returns the checked state of the checkbox.
49088      * @return {Boolean} True if checked, else false
49089      */
49090     getValue : function(){
49091         return this.el.dom.value;
49092         
49093     },
49094
49095         // private
49096     onClick : function(e){ 
49097         //this.setChecked(!this.checked);
49098         Roo.get(e.target).toggleClass('x-menu-item-checked');
49099         this.refreshValue();
49100         //if(this.el.dom.checked != this.checked){
49101         //    this.setValue(this.el.dom.checked);
49102        // }
49103     },
49104     
49105     // private
49106     refreshValue : function()
49107     {
49108         var val = '';
49109         this.viewEl.select('img',true).each(function(e,i,n)  {
49110             val += e.is(".x-menu-item-checked") ? String(n) : '';
49111         });
49112         this.setValue(val, true);
49113     },
49114
49115     /**
49116      * Sets the checked state of the checkbox.
49117      * On is always based on a string comparison between inputValue and the param.
49118      * @param {Boolean/String} value - the value to set 
49119      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49120      */
49121     setValue : function(v,suppressEvent){
49122         if (!this.el.dom) {
49123             return;
49124         }
49125         var old = this.el.dom.value ;
49126         this.el.dom.value = v;
49127         if (suppressEvent) {
49128             return ;
49129         }
49130          
49131         // update display..
49132         this.viewEl.select('img',true).each(function(e,i,n)  {
49133             
49134             var on = e.is(".x-menu-item-checked");
49135             var newv = v.indexOf(String(n)) > -1;
49136             if (on != newv) {
49137                 e.toggleClass('x-menu-item-checked');
49138             }
49139             
49140         });
49141         
49142         
49143         this.fireEvent('change', this, v, old);
49144         
49145         
49146     },
49147    
49148     // handle setting of hidden value by some other method!!?!?
49149     setFromHidden: function()
49150     {
49151         if(!this.el){
49152             return;
49153         }
49154         //console.log("SET FROM HIDDEN");
49155         //alert('setFrom hidden');
49156         this.setValue(this.el.dom.value);
49157     },
49158     
49159     onDestroy : function()
49160     {
49161         if(this.viewEl){
49162             Roo.get(this.viewEl).remove();
49163         }
49164          
49165         Roo.form.DayPicker.superclass.onDestroy.call(this);
49166     }
49167
49168 });/*
49169  * RooJS Library 1.1.1
49170  * Copyright(c) 2008-2011  Alan Knowles
49171  *
49172  * License - LGPL
49173  */
49174  
49175
49176 /**
49177  * @class Roo.form.ComboCheck
49178  * @extends Roo.form.ComboBox
49179  * A combobox for multiple select items.
49180  *
49181  * FIXME - could do with a reset button..
49182  * 
49183  * @constructor
49184  * Create a new ComboCheck
49185  * @param {Object} config Configuration options
49186  */
49187 Roo.form.ComboCheck = function(config){
49188     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49189     // should verify some data...
49190     // like
49191     // hiddenName = required..
49192     // displayField = required
49193     // valudField == required
49194     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49195     var _t = this;
49196     Roo.each(req, function(e) {
49197         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49198             throw "Roo.form.ComboCheck : missing value for: " + e;
49199         }
49200     });
49201     
49202     
49203 };
49204
49205 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49206      
49207      
49208     editable : false,
49209      
49210     selectedClass: 'x-menu-item-checked', 
49211     
49212     // private
49213     onRender : function(ct, position){
49214         var _t = this;
49215         
49216         
49217         
49218         if(!this.tpl){
49219             var cls = 'x-combo-list';
49220
49221             
49222             this.tpl =  new Roo.Template({
49223                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49224                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49225                    '<span>{' + this.displayField + '}</span>' +
49226                     '</div>' 
49227                 
49228             });
49229         }
49230  
49231         
49232         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49233         this.view.singleSelect = false;
49234         this.view.multiSelect = true;
49235         this.view.toggleSelect = true;
49236         this.pageTb.add(new Roo.Toolbar.Fill(), {
49237             
49238             text: 'Done',
49239             handler: function()
49240             {
49241                 _t.collapse();
49242             }
49243         });
49244     },
49245     
49246     onViewOver : function(e, t){
49247         // do nothing...
49248         return;
49249         
49250     },
49251     
49252     onViewClick : function(doFocus,index){
49253         return;
49254         
49255     },
49256     select: function () {
49257         //Roo.log("SELECT CALLED");
49258     },
49259      
49260     selectByValue : function(xv, scrollIntoView){
49261         var ar = this.getValueArray();
49262         var sels = [];
49263         
49264         Roo.each(ar, function(v) {
49265             if(v === undefined || v === null){
49266                 return;
49267             }
49268             var r = this.findRecord(this.valueField, v);
49269             if(r){
49270                 sels.push(this.store.indexOf(r))
49271                 
49272             }
49273         },this);
49274         this.view.select(sels);
49275         return false;
49276     },
49277     
49278     
49279     
49280     onSelect : function(record, index){
49281        // Roo.log("onselect Called");
49282        // this is only called by the clear button now..
49283         this.view.clearSelections();
49284         this.setValue('[]');
49285         if (this.value != this.valueBefore) {
49286             this.fireEvent('change', this, this.value, this.valueBefore);
49287             this.valueBefore = this.value;
49288         }
49289     },
49290     getValueArray : function()
49291     {
49292         var ar = [] ;
49293         
49294         try {
49295             //Roo.log(this.value);
49296             if (typeof(this.value) == 'undefined') {
49297                 return [];
49298             }
49299             var ar = Roo.decode(this.value);
49300             return  ar instanceof Array ? ar : []; //?? valid?
49301             
49302         } catch(e) {
49303             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49304             return [];
49305         }
49306          
49307     },
49308     expand : function ()
49309     {
49310         
49311         Roo.form.ComboCheck.superclass.expand.call(this);
49312         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49313         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49314         
49315
49316     },
49317     
49318     collapse : function(){
49319         Roo.form.ComboCheck.superclass.collapse.call(this);
49320         var sl = this.view.getSelectedIndexes();
49321         var st = this.store;
49322         var nv = [];
49323         var tv = [];
49324         var r;
49325         Roo.each(sl, function(i) {
49326             r = st.getAt(i);
49327             nv.push(r.get(this.valueField));
49328         },this);
49329         this.setValue(Roo.encode(nv));
49330         if (this.value != this.valueBefore) {
49331
49332             this.fireEvent('change', this, this.value, this.valueBefore);
49333             this.valueBefore = this.value;
49334         }
49335         
49336     },
49337     
49338     setValue : function(v){
49339         // Roo.log(v);
49340         this.value = v;
49341         
49342         var vals = this.getValueArray();
49343         var tv = [];
49344         Roo.each(vals, function(k) {
49345             var r = this.findRecord(this.valueField, k);
49346             if(r){
49347                 tv.push(r.data[this.displayField]);
49348             }else if(this.valueNotFoundText !== undefined){
49349                 tv.push( this.valueNotFoundText );
49350             }
49351         },this);
49352        // Roo.log(tv);
49353         
49354         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49355         this.hiddenField.value = v;
49356         this.value = v;
49357     }
49358     
49359 });/*
49360  * Based on:
49361  * Ext JS Library 1.1.1
49362  * Copyright(c) 2006-2007, Ext JS, LLC.
49363  *
49364  * Originally Released Under LGPL - original licence link has changed is not relivant.
49365  *
49366  * Fork - LGPL
49367  * <script type="text/javascript">
49368  */
49369  
49370 /**
49371  * @class Roo.form.Signature
49372  * @extends Roo.form.Field
49373  * Signature field.  
49374  * @constructor
49375  * 
49376  * @param {Object} config Configuration options
49377  */
49378
49379 Roo.form.Signature = function(config){
49380     Roo.form.Signature.superclass.constructor.call(this, config);
49381     
49382     this.addEvents({// not in used??
49383          /**
49384          * @event confirm
49385          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49386              * @param {Roo.form.Signature} combo This combo box
49387              */
49388         'confirm' : true,
49389         /**
49390          * @event reset
49391          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49392              * @param {Roo.form.ComboBox} combo This combo box
49393              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49394              */
49395         'reset' : true
49396     });
49397 };
49398
49399 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49400     /**
49401      * @cfg {Object} labels Label to use when rendering a form.
49402      * defaults to 
49403      * labels : { 
49404      *      clear : "Clear",
49405      *      confirm : "Confirm"
49406      *  }
49407      */
49408     labels : { 
49409         clear : "Clear",
49410         confirm : "Confirm"
49411     },
49412     /**
49413      * @cfg {Number} width The signature panel width (defaults to 300)
49414      */
49415     width: 300,
49416     /**
49417      * @cfg {Number} height The signature panel height (defaults to 100)
49418      */
49419     height : 100,
49420     /**
49421      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49422      */
49423     allowBlank : false,
49424     
49425     //private
49426     // {Object} signPanel The signature SVG panel element (defaults to {})
49427     signPanel : {},
49428     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49429     isMouseDown : false,
49430     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49431     isConfirmed : false,
49432     // {String} signatureTmp SVG mapping string (defaults to empty string)
49433     signatureTmp : '',
49434     
49435     
49436     defaultAutoCreate : { // modified by initCompnoent..
49437         tag: "input",
49438         type:"hidden"
49439     },
49440
49441     // private
49442     onRender : function(ct, position){
49443         
49444         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49445         
49446         this.wrap = this.el.wrap({
49447             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49448         });
49449         
49450         this.createToolbar(this);
49451         this.signPanel = this.wrap.createChild({
49452                 tag: 'div',
49453                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49454             }, this.el
49455         );
49456             
49457         this.svgID = Roo.id();
49458         this.svgEl = this.signPanel.createChild({
49459               xmlns : 'http://www.w3.org/2000/svg',
49460               tag : 'svg',
49461               id : this.svgID + "-svg",
49462               width: this.width,
49463               height: this.height,
49464               viewBox: '0 0 '+this.width+' '+this.height,
49465               cn : [
49466                 {
49467                     tag: "rect",
49468                     id: this.svgID + "-svg-r",
49469                     width: this.width,
49470                     height: this.height,
49471                     fill: "#ffa"
49472                 },
49473                 {
49474                     tag: "line",
49475                     id: this.svgID + "-svg-l",
49476                     x1: "0", // start
49477                     y1: (this.height*0.8), // start set the line in 80% of height
49478                     x2: this.width, // end
49479                     y2: (this.height*0.8), // end set the line in 80% of height
49480                     'stroke': "#666",
49481                     'stroke-width': "1",
49482                     'stroke-dasharray': "3",
49483                     'shape-rendering': "crispEdges",
49484                     'pointer-events': "none"
49485                 },
49486                 {
49487                     tag: "path",
49488                     id: this.svgID + "-svg-p",
49489                     'stroke': "navy",
49490                     'stroke-width': "3",
49491                     'fill': "none",
49492                     'pointer-events': 'none'
49493                 }
49494               ]
49495         });
49496         this.createSVG();
49497         this.svgBox = this.svgEl.dom.getScreenCTM();
49498     },
49499     createSVG : function(){ 
49500         var svg = this.signPanel;
49501         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49502         var t = this;
49503
49504         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49505         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49506         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49507         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49508         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49509         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49510         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49511         
49512     },
49513     isTouchEvent : function(e){
49514         return e.type.match(/^touch/);
49515     },
49516     getCoords : function (e) {
49517         var pt    = this.svgEl.dom.createSVGPoint();
49518         pt.x = e.clientX; 
49519         pt.y = e.clientY;
49520         if (this.isTouchEvent(e)) {
49521             pt.x =  e.targetTouches[0].clientX;
49522             pt.y = e.targetTouches[0].clientY;
49523         }
49524         var a = this.svgEl.dom.getScreenCTM();
49525         var b = a.inverse();
49526         var mx = pt.matrixTransform(b);
49527         return mx.x + ',' + mx.y;
49528     },
49529     //mouse event headler 
49530     down : function (e) {
49531         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49532         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49533         
49534         this.isMouseDown = true;
49535         
49536         e.preventDefault();
49537     },
49538     move : function (e) {
49539         if (this.isMouseDown) {
49540             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49541             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49542         }
49543         
49544         e.preventDefault();
49545     },
49546     up : function (e) {
49547         this.isMouseDown = false;
49548         var sp = this.signatureTmp.split(' ');
49549         
49550         if(sp.length > 1){
49551             if(!sp[sp.length-2].match(/^L/)){
49552                 sp.pop();
49553                 sp.pop();
49554                 sp.push("");
49555                 this.signatureTmp = sp.join(" ");
49556             }
49557         }
49558         if(this.getValue() != this.signatureTmp){
49559             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49560             this.isConfirmed = false;
49561         }
49562         e.preventDefault();
49563     },
49564     
49565     /**
49566      * Protected method that will not generally be called directly. It
49567      * is called when the editor creates its toolbar. Override this method if you need to
49568      * add custom toolbar buttons.
49569      * @param {HtmlEditor} editor
49570      */
49571     createToolbar : function(editor){
49572          function btn(id, toggle, handler){
49573             var xid = fid + '-'+ id ;
49574             return {
49575                 id : xid,
49576                 cmd : id,
49577                 cls : 'x-btn-icon x-edit-'+id,
49578                 enableToggle:toggle !== false,
49579                 scope: editor, // was editor...
49580                 handler:handler||editor.relayBtnCmd,
49581                 clickEvent:'mousedown',
49582                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49583                 tabIndex:-1
49584             };
49585         }
49586         
49587         
49588         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49589         this.tb = tb;
49590         this.tb.add(
49591            {
49592                 cls : ' x-signature-btn x-signature-'+id,
49593                 scope: editor, // was editor...
49594                 handler: this.reset,
49595                 clickEvent:'mousedown',
49596                 text: this.labels.clear
49597             },
49598             {
49599                  xtype : 'Fill',
49600                  xns: Roo.Toolbar
49601             }, 
49602             {
49603                 cls : '  x-signature-btn x-signature-'+id,
49604                 scope: editor, // was editor...
49605                 handler: this.confirmHandler,
49606                 clickEvent:'mousedown',
49607                 text: this.labels.confirm
49608             }
49609         );
49610     
49611     },
49612     //public
49613     /**
49614      * when user is clicked confirm then show this image.....
49615      * 
49616      * @return {String} Image Data URI
49617      */
49618     getImageDataURI : function(){
49619         var svg = this.svgEl.dom.parentNode.innerHTML;
49620         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49621         return src; 
49622     },
49623     /**
49624      * 
49625      * @return {Boolean} this.isConfirmed
49626      */
49627     getConfirmed : function(){
49628         return this.isConfirmed;
49629     },
49630     /**
49631      * 
49632      * @return {Number} this.width
49633      */
49634     getWidth : function(){
49635         return this.width;
49636     },
49637     /**
49638      * 
49639      * @return {Number} this.height
49640      */
49641     getHeight : function(){
49642         return this.height;
49643     },
49644     // private
49645     getSignature : function(){
49646         return this.signatureTmp;
49647     },
49648     // private
49649     reset : function(){
49650         this.signatureTmp = '';
49651         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49652         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49653         this.isConfirmed = false;
49654         Roo.form.Signature.superclass.reset.call(this);
49655     },
49656     setSignature : function(s){
49657         this.signatureTmp = s;
49658         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49659         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49660         this.setValue(s);
49661         this.isConfirmed = false;
49662         Roo.form.Signature.superclass.reset.call(this);
49663     }, 
49664     test : function(){
49665 //        Roo.log(this.signPanel.dom.contentWindow.up())
49666     },
49667     //private
49668     setConfirmed : function(){
49669         
49670         
49671         
49672 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49673     },
49674     // private
49675     confirmHandler : function(){
49676         if(!this.getSignature()){
49677             return;
49678         }
49679         
49680         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49681         this.setValue(this.getSignature());
49682         this.isConfirmed = true;
49683         
49684         this.fireEvent('confirm', this);
49685     },
49686     // private
49687     // Subclasses should provide the validation implementation by overriding this
49688     validateValue : function(value){
49689         if(this.allowBlank){
49690             return true;
49691         }
49692         
49693         if(this.isConfirmed){
49694             return true;
49695         }
49696         return false;
49697     }
49698 });/*
49699  * Based on:
49700  * Ext JS Library 1.1.1
49701  * Copyright(c) 2006-2007, Ext JS, LLC.
49702  *
49703  * Originally Released Under LGPL - original licence link has changed is not relivant.
49704  *
49705  * Fork - LGPL
49706  * <script type="text/javascript">
49707  */
49708  
49709
49710 /**
49711  * @class Roo.form.ComboBox
49712  * @extends Roo.form.TriggerField
49713  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49714  * @constructor
49715  * Create a new ComboBox.
49716  * @param {Object} config Configuration options
49717  */
49718 Roo.form.Select = function(config){
49719     Roo.form.Select.superclass.constructor.call(this, config);
49720      
49721 };
49722
49723 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49724     /**
49725      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49726      */
49727     /**
49728      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49729      * rendering into an Roo.Editor, defaults to false)
49730      */
49731     /**
49732      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49733      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49734      */
49735     /**
49736      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49737      */
49738     /**
49739      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49740      * the dropdown list (defaults to undefined, with no header element)
49741      */
49742
49743      /**
49744      * @cfg {String/Roo.Template} tpl The template to use to render the output
49745      */
49746      
49747     // private
49748     defaultAutoCreate : {tag: "select"  },
49749     /**
49750      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49751      */
49752     listWidth: undefined,
49753     /**
49754      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49755      * mode = 'remote' or 'text' if mode = 'local')
49756      */
49757     displayField: undefined,
49758     /**
49759      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49760      * mode = 'remote' or 'value' if mode = 'local'). 
49761      * Note: use of a valueField requires the user make a selection
49762      * in order for a value to be mapped.
49763      */
49764     valueField: undefined,
49765     
49766     
49767     /**
49768      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49769      * field's data value (defaults to the underlying DOM element's name)
49770      */
49771     hiddenName: undefined,
49772     /**
49773      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49774      */
49775     listClass: '',
49776     /**
49777      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49778      */
49779     selectedClass: 'x-combo-selected',
49780     /**
49781      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49782      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49783      * which displays a downward arrow icon).
49784      */
49785     triggerClass : 'x-form-arrow-trigger',
49786     /**
49787      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49788      */
49789     shadow:'sides',
49790     /**
49791      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49792      * anchor positions (defaults to 'tl-bl')
49793      */
49794     listAlign: 'tl-bl?',
49795     /**
49796      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49797      */
49798     maxHeight: 300,
49799     /**
49800      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49801      * query specified by the allQuery config option (defaults to 'query')
49802      */
49803     triggerAction: 'query',
49804     /**
49805      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49806      * (defaults to 4, does not apply if editable = false)
49807      */
49808     minChars : 4,
49809     /**
49810      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49811      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49812      */
49813     typeAhead: false,
49814     /**
49815      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49816      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49817      */
49818     queryDelay: 500,
49819     /**
49820      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49821      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49822      */
49823     pageSize: 0,
49824     /**
49825      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49826      * when editable = true (defaults to false)
49827      */
49828     selectOnFocus:false,
49829     /**
49830      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49831      */
49832     queryParam: 'query',
49833     /**
49834      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49835      * when mode = 'remote' (defaults to 'Loading...')
49836      */
49837     loadingText: 'Loading...',
49838     /**
49839      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49840      */
49841     resizable: false,
49842     /**
49843      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49844      */
49845     handleHeight : 8,
49846     /**
49847      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49848      * traditional select (defaults to true)
49849      */
49850     editable: true,
49851     /**
49852      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49853      */
49854     allQuery: '',
49855     /**
49856      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49857      */
49858     mode: 'remote',
49859     /**
49860      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49861      * listWidth has a higher value)
49862      */
49863     minListWidth : 70,
49864     /**
49865      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49866      * allow the user to set arbitrary text into the field (defaults to false)
49867      */
49868     forceSelection:false,
49869     /**
49870      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49871      * if typeAhead = true (defaults to 250)
49872      */
49873     typeAheadDelay : 250,
49874     /**
49875      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49876      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49877      */
49878     valueNotFoundText : undefined,
49879     
49880     /**
49881      * @cfg {String} defaultValue The value displayed after loading the store.
49882      */
49883     defaultValue: '',
49884     
49885     /**
49886      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49887      */
49888     blockFocus : false,
49889     
49890     /**
49891      * @cfg {Boolean} disableClear Disable showing of clear button.
49892      */
49893     disableClear : false,
49894     /**
49895      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49896      */
49897     alwaysQuery : false,
49898     
49899     //private
49900     addicon : false,
49901     editicon: false,
49902     
49903     // element that contains real text value.. (when hidden is used..)
49904      
49905     // private
49906     onRender : function(ct, position){
49907         Roo.form.Field.prototype.onRender.call(this, ct, position);
49908         
49909         if(this.store){
49910             this.store.on('beforeload', this.onBeforeLoad, this);
49911             this.store.on('load', this.onLoad, this);
49912             this.store.on('loadexception', this.onLoadException, this);
49913             this.store.load({});
49914         }
49915         
49916         
49917         
49918     },
49919
49920     // private
49921     initEvents : function(){
49922         //Roo.form.ComboBox.superclass.initEvents.call(this);
49923  
49924     },
49925
49926     onDestroy : function(){
49927        
49928         if(this.store){
49929             this.store.un('beforeload', this.onBeforeLoad, this);
49930             this.store.un('load', this.onLoad, this);
49931             this.store.un('loadexception', this.onLoadException, this);
49932         }
49933         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49934     },
49935
49936     // private
49937     fireKey : function(e){
49938         if(e.isNavKeyPress() && !this.list.isVisible()){
49939             this.fireEvent("specialkey", this, e);
49940         }
49941     },
49942
49943     // private
49944     onResize: function(w, h){
49945         
49946         return; 
49947     
49948         
49949     },
49950
49951     /**
49952      * Allow or prevent the user from directly editing the field text.  If false is passed,
49953      * the user will only be able to select from the items defined in the dropdown list.  This method
49954      * is the runtime equivalent of setting the 'editable' config option at config time.
49955      * @param {Boolean} value True to allow the user to directly edit the field text
49956      */
49957     setEditable : function(value){
49958          
49959     },
49960
49961     // private
49962     onBeforeLoad : function(){
49963         
49964         Roo.log("Select before load");
49965         return;
49966     
49967         this.innerList.update(this.loadingText ?
49968                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
49969         //this.restrictHeight();
49970         this.selectedIndex = -1;
49971     },
49972
49973     // private
49974     onLoad : function(){
49975
49976     
49977         var dom = this.el.dom;
49978         dom.innerHTML = '';
49979          var od = dom.ownerDocument;
49980          
49981         if (this.emptyText) {
49982             var op = od.createElement('option');
49983             op.setAttribute('value', '');
49984             op.innerHTML = String.format('{0}', this.emptyText);
49985             dom.appendChild(op);
49986         }
49987         if(this.store.getCount() > 0){
49988            
49989             var vf = this.valueField;
49990             var df = this.displayField;
49991             this.store.data.each(function(r) {
49992                 // which colmsn to use... testing - cdoe / title..
49993                 var op = od.createElement('option');
49994                 op.setAttribute('value', r.data[vf]);
49995                 op.innerHTML = String.format('{0}', r.data[df]);
49996                 dom.appendChild(op);
49997             });
49998             if (typeof(this.defaultValue != 'undefined')) {
49999                 this.setValue(this.defaultValue);
50000             }
50001             
50002              
50003         }else{
50004             //this.onEmptyResults();
50005         }
50006         //this.el.focus();
50007     },
50008     // private
50009     onLoadException : function()
50010     {
50011         dom.innerHTML = '';
50012             
50013         Roo.log("Select on load exception");
50014         return;
50015     
50016         this.collapse();
50017         Roo.log(this.store.reader.jsonData);
50018         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50019             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50020         }
50021         
50022         
50023     },
50024     // private
50025     onTypeAhead : function(){
50026          
50027     },
50028
50029     // private
50030     onSelect : function(record, index){
50031         Roo.log('on select?');
50032         return;
50033         if(this.fireEvent('beforeselect', this, record, index) !== false){
50034             this.setFromData(index > -1 ? record.data : false);
50035             this.collapse();
50036             this.fireEvent('select', this, record, index);
50037         }
50038     },
50039
50040     /**
50041      * Returns the currently selected field value or empty string if no value is set.
50042      * @return {String} value The selected value
50043      */
50044     getValue : function(){
50045         var dom = this.el.dom;
50046         this.value = dom.options[dom.selectedIndex].value;
50047         return this.value;
50048         
50049     },
50050
50051     /**
50052      * Clears any text/value currently set in the field
50053      */
50054     clearValue : function(){
50055         this.value = '';
50056         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50057         
50058     },
50059
50060     /**
50061      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50062      * will be displayed in the field.  If the value does not match the data value of an existing item,
50063      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50064      * Otherwise the field will be blank (although the value will still be set).
50065      * @param {String} value The value to match
50066      */
50067     setValue : function(v){
50068         var d = this.el.dom;
50069         for (var i =0; i < d.options.length;i++) {
50070             if (v == d.options[i].value) {
50071                 d.selectedIndex = i;
50072                 this.value = v;
50073                 return;
50074             }
50075         }
50076         this.clearValue();
50077     },
50078     /**
50079      * @property {Object} the last set data for the element
50080      */
50081     
50082     lastData : false,
50083     /**
50084      * Sets the value of the field based on a object which is related to the record format for the store.
50085      * @param {Object} value the value to set as. or false on reset?
50086      */
50087     setFromData : function(o){
50088         Roo.log('setfrom data?');
50089          
50090         
50091         
50092     },
50093     // private
50094     reset : function(){
50095         this.clearValue();
50096     },
50097     // private
50098     findRecord : function(prop, value){
50099         
50100         return false;
50101     
50102         var record;
50103         if(this.store.getCount() > 0){
50104             this.store.each(function(r){
50105                 if(r.data[prop] == value){
50106                     record = r;
50107                     return false;
50108                 }
50109                 return true;
50110             });
50111         }
50112         return record;
50113     },
50114     
50115     getName: function()
50116     {
50117         // returns hidden if it's set..
50118         if (!this.rendered) {return ''};
50119         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50120         
50121     },
50122      
50123
50124     
50125
50126     // private
50127     onEmptyResults : function(){
50128         Roo.log('empty results');
50129         //this.collapse();
50130     },
50131
50132     /**
50133      * Returns true if the dropdown list is expanded, else false.
50134      */
50135     isExpanded : function(){
50136         return false;
50137     },
50138
50139     /**
50140      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50141      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50142      * @param {String} value The data value of the item to select
50143      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50144      * selected item if it is not currently in view (defaults to true)
50145      * @return {Boolean} True if the value matched an item in the list, else false
50146      */
50147     selectByValue : function(v, scrollIntoView){
50148         Roo.log('select By Value');
50149         return false;
50150     
50151         if(v !== undefined && v !== null){
50152             var r = this.findRecord(this.valueField || this.displayField, v);
50153             if(r){
50154                 this.select(this.store.indexOf(r), scrollIntoView);
50155                 return true;
50156             }
50157         }
50158         return false;
50159     },
50160
50161     /**
50162      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50163      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50164      * @param {Number} index The zero-based index of the list item to select
50165      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50166      * selected item if it is not currently in view (defaults to true)
50167      */
50168     select : function(index, scrollIntoView){
50169         Roo.log('select ');
50170         return  ;
50171         
50172         this.selectedIndex = index;
50173         this.view.select(index);
50174         if(scrollIntoView !== false){
50175             var el = this.view.getNode(index);
50176             if(el){
50177                 this.innerList.scrollChildIntoView(el, false);
50178             }
50179         }
50180     },
50181
50182       
50183
50184     // private
50185     validateBlur : function(){
50186         
50187         return;
50188         
50189     },
50190
50191     // private
50192     initQuery : function(){
50193         this.doQuery(this.getRawValue());
50194     },
50195
50196     // private
50197     doForce : function(){
50198         if(this.el.dom.value.length > 0){
50199             this.el.dom.value =
50200                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50201              
50202         }
50203     },
50204
50205     /**
50206      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50207      * query allowing the query action to be canceled if needed.
50208      * @param {String} query The SQL query to execute
50209      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50210      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50211      * saved in the current store (defaults to false)
50212      */
50213     doQuery : function(q, forceAll){
50214         
50215         Roo.log('doQuery?');
50216         if(q === undefined || q === null){
50217             q = '';
50218         }
50219         var qe = {
50220             query: q,
50221             forceAll: forceAll,
50222             combo: this,
50223             cancel:false
50224         };
50225         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50226             return false;
50227         }
50228         q = qe.query;
50229         forceAll = qe.forceAll;
50230         if(forceAll === true || (q.length >= this.minChars)){
50231             if(this.lastQuery != q || this.alwaysQuery){
50232                 this.lastQuery = q;
50233                 if(this.mode == 'local'){
50234                     this.selectedIndex = -1;
50235                     if(forceAll){
50236                         this.store.clearFilter();
50237                     }else{
50238                         this.store.filter(this.displayField, q);
50239                     }
50240                     this.onLoad();
50241                 }else{
50242                     this.store.baseParams[this.queryParam] = q;
50243                     this.store.load({
50244                         params: this.getParams(q)
50245                     });
50246                     this.expand();
50247                 }
50248             }else{
50249                 this.selectedIndex = -1;
50250                 this.onLoad();   
50251             }
50252         }
50253     },
50254
50255     // private
50256     getParams : function(q){
50257         var p = {};
50258         //p[this.queryParam] = q;
50259         if(this.pageSize){
50260             p.start = 0;
50261             p.limit = this.pageSize;
50262         }
50263         return p;
50264     },
50265
50266     /**
50267      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50268      */
50269     collapse : function(){
50270         
50271     },
50272
50273     // private
50274     collapseIf : function(e){
50275         
50276     },
50277
50278     /**
50279      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50280      */
50281     expand : function(){
50282         
50283     } ,
50284
50285     // private
50286      
50287
50288     /** 
50289     * @cfg {Boolean} grow 
50290     * @hide 
50291     */
50292     /** 
50293     * @cfg {Number} growMin 
50294     * @hide 
50295     */
50296     /** 
50297     * @cfg {Number} growMax 
50298     * @hide 
50299     */
50300     /**
50301      * @hide
50302      * @method autoSize
50303      */
50304     
50305     setWidth : function()
50306     {
50307         
50308     },
50309     getResizeEl : function(){
50310         return this.el;
50311     }
50312 });//<script type="text/javasscript">
50313  
50314
50315 /**
50316  * @class Roo.DDView
50317  * A DnD enabled version of Roo.View.
50318  * @param {Element/String} container The Element in which to create the View.
50319  * @param {String} tpl The template string used to create the markup for each element of the View
50320  * @param {Object} config The configuration properties. These include all the config options of
50321  * {@link Roo.View} plus some specific to this class.<br>
50322  * <p>
50323  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50324  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50325  * <p>
50326  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50327 .x-view-drag-insert-above {
50328         border-top:1px dotted #3366cc;
50329 }
50330 .x-view-drag-insert-below {
50331         border-bottom:1px dotted #3366cc;
50332 }
50333 </code></pre>
50334  * 
50335  */
50336  
50337 Roo.DDView = function(container, tpl, config) {
50338     Roo.DDView.superclass.constructor.apply(this, arguments);
50339     this.getEl().setStyle("outline", "0px none");
50340     this.getEl().unselectable();
50341     if (this.dragGroup) {
50342                 this.setDraggable(this.dragGroup.split(","));
50343     }
50344     if (this.dropGroup) {
50345                 this.setDroppable(this.dropGroup.split(","));
50346     }
50347     if (this.deletable) {
50348         this.setDeletable();
50349     }
50350     this.isDirtyFlag = false;
50351         this.addEvents({
50352                 "drop" : true
50353         });
50354 };
50355
50356 Roo.extend(Roo.DDView, Roo.View, {
50357 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50358 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50359 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50360 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50361
50362         isFormField: true,
50363
50364         reset: Roo.emptyFn,
50365         
50366         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50367
50368         validate: function() {
50369                 return true;
50370         },
50371         
50372         destroy: function() {
50373                 this.purgeListeners();
50374                 this.getEl.removeAllListeners();
50375                 this.getEl().remove();
50376                 if (this.dragZone) {
50377                         if (this.dragZone.destroy) {
50378                                 this.dragZone.destroy();
50379                         }
50380                 }
50381                 if (this.dropZone) {
50382                         if (this.dropZone.destroy) {
50383                                 this.dropZone.destroy();
50384                         }
50385                 }
50386         },
50387
50388 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50389         getName: function() {
50390                 return this.name;
50391         },
50392
50393 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50394         setValue: function(v) {
50395                 if (!this.store) {
50396                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50397                 }
50398                 var data = {};
50399                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50400                 this.store.proxy = new Roo.data.MemoryProxy(data);
50401                 this.store.load();
50402         },
50403
50404 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50405         getValue: function() {
50406                 var result = '(';
50407                 this.store.each(function(rec) {
50408                         result += rec.id + ',';
50409                 });
50410                 return result.substr(0, result.length - 1) + ')';
50411         },
50412         
50413         getIds: function() {
50414                 var i = 0, result = new Array(this.store.getCount());
50415                 this.store.each(function(rec) {
50416                         result[i++] = rec.id;
50417                 });
50418                 return result;
50419         },
50420         
50421         isDirty: function() {
50422                 return this.isDirtyFlag;
50423         },
50424
50425 /**
50426  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50427  *      whole Element becomes the target, and this causes the drop gesture to append.
50428  */
50429     getTargetFromEvent : function(e) {
50430                 var target = e.getTarget();
50431                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50432                 target = target.parentNode;
50433                 }
50434                 if (!target) {
50435                         target = this.el.dom.lastChild || this.el.dom;
50436                 }
50437                 return target;
50438     },
50439
50440 /**
50441  *      Create the drag data which consists of an object which has the property "ddel" as
50442  *      the drag proxy element. 
50443  */
50444     getDragData : function(e) {
50445         var target = this.findItemFromChild(e.getTarget());
50446                 if(target) {
50447                         this.handleSelection(e);
50448                         var selNodes = this.getSelectedNodes();
50449             var dragData = {
50450                 source: this,
50451                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50452                 nodes: selNodes,
50453                 records: []
50454                         };
50455                         var selectedIndices = this.getSelectedIndexes();
50456                         for (var i = 0; i < selectedIndices.length; i++) {
50457                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50458                         }
50459                         if (selNodes.length == 1) {
50460                                 dragData.ddel = target.cloneNode(true); // the div element
50461                         } else {
50462                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50463                                 div.className = 'multi-proxy';
50464                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50465                                         div.appendChild(selNodes[i].cloneNode(true));
50466                                 }
50467                                 dragData.ddel = div;
50468                         }
50469             //console.log(dragData)
50470             //console.log(dragData.ddel.innerHTML)
50471                         return dragData;
50472                 }
50473         //console.log('nodragData')
50474                 return false;
50475     },
50476     
50477 /**     Specify to which ddGroup items in this DDView may be dragged. */
50478     setDraggable: function(ddGroup) {
50479         if (ddGroup instanceof Array) {
50480                 Roo.each(ddGroup, this.setDraggable, this);
50481                 return;
50482         }
50483         if (this.dragZone) {
50484                 this.dragZone.addToGroup(ddGroup);
50485         } else {
50486                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50487                                 containerScroll: true,
50488                                 ddGroup: ddGroup 
50489
50490                         });
50491 //                      Draggability implies selection. DragZone's mousedown selects the element.
50492                         if (!this.multiSelect) { this.singleSelect = true; }
50493
50494 //                      Wire the DragZone's handlers up to methods in *this*
50495                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50496                 }
50497     },
50498
50499 /**     Specify from which ddGroup this DDView accepts drops. */
50500     setDroppable: function(ddGroup) {
50501         if (ddGroup instanceof Array) {
50502                 Roo.each(ddGroup, this.setDroppable, this);
50503                 return;
50504         }
50505         if (this.dropZone) {
50506                 this.dropZone.addToGroup(ddGroup);
50507         } else {
50508                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50509                                 containerScroll: true,
50510                                 ddGroup: ddGroup
50511                         });
50512
50513 //                      Wire the DropZone's handlers up to methods in *this*
50514                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50515                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50516                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50517                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50518                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50519                 }
50520     },
50521
50522 /**     Decide whether to drop above or below a View node. */
50523     getDropPoint : function(e, n, dd){
50524         if (n == this.el.dom) { return "above"; }
50525                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50526                 var c = t + (b - t) / 2;
50527                 var y = Roo.lib.Event.getPageY(e);
50528                 if(y <= c) {
50529                         return "above";
50530                 }else{
50531                         return "below";
50532                 }
50533     },
50534
50535     onNodeEnter : function(n, dd, e, data){
50536                 return false;
50537     },
50538     
50539     onNodeOver : function(n, dd, e, data){
50540                 var pt = this.getDropPoint(e, n, dd);
50541                 // set the insert point style on the target node
50542                 var dragElClass = this.dropNotAllowed;
50543                 if (pt) {
50544                         var targetElClass;
50545                         if (pt == "above"){
50546                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50547                                 targetElClass = "x-view-drag-insert-above";
50548                         } else {
50549                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50550                                 targetElClass = "x-view-drag-insert-below";
50551                         }
50552                         if (this.lastInsertClass != targetElClass){
50553                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50554                                 this.lastInsertClass = targetElClass;
50555                         }
50556                 }
50557                 return dragElClass;
50558         },
50559
50560     onNodeOut : function(n, dd, e, data){
50561                 this.removeDropIndicators(n);
50562     },
50563
50564     onNodeDrop : function(n, dd, e, data){
50565         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50566                 return false;
50567         }
50568         var pt = this.getDropPoint(e, n, dd);
50569                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50570                 if (pt == "below") { insertAt++; }
50571                 for (var i = 0; i < data.records.length; i++) {
50572                         var r = data.records[i];
50573                         var dup = this.store.getById(r.id);
50574                         if (dup && (dd != this.dragZone)) {
50575                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50576                         } else {
50577                                 if (data.copy) {
50578                                         this.store.insert(insertAt++, r.copy());
50579                                 } else {
50580                                         data.source.isDirtyFlag = true;
50581                                         r.store.remove(r);
50582                                         this.store.insert(insertAt++, r);
50583                                 }
50584                                 this.isDirtyFlag = true;
50585                         }
50586                 }
50587                 this.dragZone.cachedTarget = null;
50588                 return true;
50589     },
50590
50591     removeDropIndicators : function(n){
50592                 if(n){
50593                         Roo.fly(n).removeClass([
50594                                 "x-view-drag-insert-above",
50595                                 "x-view-drag-insert-below"]);
50596                         this.lastInsertClass = "_noclass";
50597                 }
50598     },
50599
50600 /**
50601  *      Utility method. Add a delete option to the DDView's context menu.
50602  *      @param {String} imageUrl The URL of the "delete" icon image.
50603  */
50604         setDeletable: function(imageUrl) {
50605                 if (!this.singleSelect && !this.multiSelect) {
50606                         this.singleSelect = true;
50607                 }
50608                 var c = this.getContextMenu();
50609                 this.contextMenu.on("itemclick", function(item) {
50610                         switch (item.id) {
50611                                 case "delete":
50612                                         this.remove(this.getSelectedIndexes());
50613                                         break;
50614                         }
50615                 }, this);
50616                 this.contextMenu.add({
50617                         icon: imageUrl,
50618                         id: "delete",
50619                         text: 'Delete'
50620                 });
50621         },
50622         
50623 /**     Return the context menu for this DDView. */
50624         getContextMenu: function() {
50625                 if (!this.contextMenu) {
50626 //                      Create the View's context menu
50627                         this.contextMenu = new Roo.menu.Menu({
50628                                 id: this.id + "-contextmenu"
50629                         });
50630                         this.el.on("contextmenu", this.showContextMenu, this);
50631                 }
50632                 return this.contextMenu;
50633         },
50634         
50635         disableContextMenu: function() {
50636                 if (this.contextMenu) {
50637                         this.el.un("contextmenu", this.showContextMenu, this);
50638                 }
50639         },
50640
50641         showContextMenu: function(e, item) {
50642         item = this.findItemFromChild(e.getTarget());
50643                 if (item) {
50644                         e.stopEvent();
50645                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50646                         this.contextMenu.showAt(e.getXY());
50647             }
50648     },
50649
50650 /**
50651  *      Remove {@link Roo.data.Record}s at the specified indices.
50652  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50653  */
50654     remove: function(selectedIndices) {
50655                 selectedIndices = [].concat(selectedIndices);
50656                 for (var i = 0; i < selectedIndices.length; i++) {
50657                         var rec = this.store.getAt(selectedIndices[i]);
50658                         this.store.remove(rec);
50659                 }
50660     },
50661
50662 /**
50663  *      Double click fires the event, but also, if this is draggable, and there is only one other
50664  *      related DropZone, it transfers the selected node.
50665  */
50666     onDblClick : function(e){
50667         var item = this.findItemFromChild(e.getTarget());
50668         if(item){
50669             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50670                 return false;
50671             }
50672             if (this.dragGroup) {
50673                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50674                     while (targets.indexOf(this.dropZone) > -1) {
50675                             targets.remove(this.dropZone);
50676                                 }
50677                     if (targets.length == 1) {
50678                                         this.dragZone.cachedTarget = null;
50679                         var el = Roo.get(targets[0].getEl());
50680                         var box = el.getBox(true);
50681                         targets[0].onNodeDrop(el.dom, {
50682                                 target: el.dom,
50683                                 xy: [box.x, box.y + box.height - 1]
50684                         }, null, this.getDragData(e));
50685                     }
50686                 }
50687         }
50688     },
50689     
50690     handleSelection: function(e) {
50691                 this.dragZone.cachedTarget = null;
50692         var item = this.findItemFromChild(e.getTarget());
50693         if (!item) {
50694                 this.clearSelections(true);
50695                 return;
50696         }
50697                 if (item && (this.multiSelect || this.singleSelect)){
50698                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50699                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50700                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50701                                 this.unselect(item);
50702                         } else {
50703                                 this.select(item, this.multiSelect && e.ctrlKey);
50704                                 this.lastSelection = item;
50705                         }
50706                 }
50707     },
50708
50709     onItemClick : function(item, index, e){
50710                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50711                         return false;
50712                 }
50713                 return true;
50714     },
50715
50716     unselect : function(nodeInfo, suppressEvent){
50717                 var node = this.getNode(nodeInfo);
50718                 if(node && this.isSelected(node)){
50719                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50720                                 Roo.fly(node).removeClass(this.selectedClass);
50721                                 this.selections.remove(node);
50722                                 if(!suppressEvent){
50723                                         this.fireEvent("selectionchange", this, this.selections);
50724                                 }
50725                         }
50726                 }
50727     }
50728 });
50729 /*
50730  * Based on:
50731  * Ext JS Library 1.1.1
50732  * Copyright(c) 2006-2007, Ext JS, LLC.
50733  *
50734  * Originally Released Under LGPL - original licence link has changed is not relivant.
50735  *
50736  * Fork - LGPL
50737  * <script type="text/javascript">
50738  */
50739  
50740 /**
50741  * @class Roo.LayoutManager
50742  * @extends Roo.util.Observable
50743  * Base class for layout managers.
50744  */
50745 Roo.LayoutManager = function(container, config){
50746     Roo.LayoutManager.superclass.constructor.call(this);
50747     this.el = Roo.get(container);
50748     // ie scrollbar fix
50749     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50750         document.body.scroll = "no";
50751     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50752         this.el.position('relative');
50753     }
50754     this.id = this.el.id;
50755     this.el.addClass("x-layout-container");
50756     /** false to disable window resize monitoring @type Boolean */
50757     this.monitorWindowResize = true;
50758     this.regions = {};
50759     this.addEvents({
50760         /**
50761          * @event layout
50762          * Fires when a layout is performed. 
50763          * @param {Roo.LayoutManager} this
50764          */
50765         "layout" : true,
50766         /**
50767          * @event regionresized
50768          * Fires when the user resizes a region. 
50769          * @param {Roo.LayoutRegion} region The resized region
50770          * @param {Number} newSize The new size (width for east/west, height for north/south)
50771          */
50772         "regionresized" : true,
50773         /**
50774          * @event regioncollapsed
50775          * Fires when a region is collapsed. 
50776          * @param {Roo.LayoutRegion} region The collapsed region
50777          */
50778         "regioncollapsed" : true,
50779         /**
50780          * @event regionexpanded
50781          * Fires when a region is expanded.  
50782          * @param {Roo.LayoutRegion} region The expanded region
50783          */
50784         "regionexpanded" : true
50785     });
50786     this.updating = false;
50787     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50788 };
50789
50790 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50791     /**
50792      * Returns true if this layout is currently being updated
50793      * @return {Boolean}
50794      */
50795     isUpdating : function(){
50796         return this.updating; 
50797     },
50798     
50799     /**
50800      * Suspend the LayoutManager from doing auto-layouts while
50801      * making multiple add or remove calls
50802      */
50803     beginUpdate : function(){
50804         this.updating = true;    
50805     },
50806     
50807     /**
50808      * Restore auto-layouts and optionally disable the manager from performing a layout
50809      * @param {Boolean} noLayout true to disable a layout update 
50810      */
50811     endUpdate : function(noLayout){
50812         this.updating = false;
50813         if(!noLayout){
50814             this.layout();
50815         }    
50816     },
50817     
50818     layout: function(){
50819         
50820     },
50821     
50822     onRegionResized : function(region, newSize){
50823         this.fireEvent("regionresized", region, newSize);
50824         this.layout();
50825     },
50826     
50827     onRegionCollapsed : function(region){
50828         this.fireEvent("regioncollapsed", region);
50829     },
50830     
50831     onRegionExpanded : function(region){
50832         this.fireEvent("regionexpanded", region);
50833     },
50834         
50835     /**
50836      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50837      * performs box-model adjustments.
50838      * @return {Object} The size as an object {width: (the width), height: (the height)}
50839      */
50840     getViewSize : function(){
50841         var size;
50842         if(this.el.dom != document.body){
50843             size = this.el.getSize();
50844         }else{
50845             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50846         }
50847         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50848         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50849         return size;
50850     },
50851     
50852     /**
50853      * Returns the Element this layout is bound to.
50854      * @return {Roo.Element}
50855      */
50856     getEl : function(){
50857         return this.el;
50858     },
50859     
50860     /**
50861      * Returns the specified region.
50862      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50863      * @return {Roo.LayoutRegion}
50864      */
50865     getRegion : function(target){
50866         return this.regions[target.toLowerCase()];
50867     },
50868     
50869     onWindowResize : function(){
50870         if(this.monitorWindowResize){
50871             this.layout();
50872         }
50873     }
50874 });/*
50875  * Based on:
50876  * Ext JS Library 1.1.1
50877  * Copyright(c) 2006-2007, Ext JS, LLC.
50878  *
50879  * Originally Released Under LGPL - original licence link has changed is not relivant.
50880  *
50881  * Fork - LGPL
50882  * <script type="text/javascript">
50883  */
50884 /**
50885  * @class Roo.BorderLayout
50886  * @extends Roo.LayoutManager
50887  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50888  * please see: <br><br>
50889  * <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>
50890  * <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>
50891  * Example:
50892  <pre><code>
50893  var layout = new Roo.BorderLayout(document.body, {
50894     north: {
50895         initialSize: 25,
50896         titlebar: false
50897     },
50898     west: {
50899         split:true,
50900         initialSize: 200,
50901         minSize: 175,
50902         maxSize: 400,
50903         titlebar: true,
50904         collapsible: true
50905     },
50906     east: {
50907         split:true,
50908         initialSize: 202,
50909         minSize: 175,
50910         maxSize: 400,
50911         titlebar: true,
50912         collapsible: true
50913     },
50914     south: {
50915         split:true,
50916         initialSize: 100,
50917         minSize: 100,
50918         maxSize: 200,
50919         titlebar: true,
50920         collapsible: true
50921     },
50922     center: {
50923         titlebar: true,
50924         autoScroll:true,
50925         resizeTabs: true,
50926         minTabWidth: 50,
50927         preferredTabWidth: 150
50928     }
50929 });
50930
50931 // shorthand
50932 var CP = Roo.ContentPanel;
50933
50934 layout.beginUpdate();
50935 layout.add("north", new CP("north", "North"));
50936 layout.add("south", new CP("south", {title: "South", closable: true}));
50937 layout.add("west", new CP("west", {title: "West"}));
50938 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50939 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50940 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50941 layout.getRegion("center").showPanel("center1");
50942 layout.endUpdate();
50943 </code></pre>
50944
50945 <b>The container the layout is rendered into can be either the body element or any other element.
50946 If it is not the body element, the container needs to either be an absolute positioned element,
50947 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50948 the container size if it is not the body element.</b>
50949
50950 * @constructor
50951 * Create a new BorderLayout
50952 * @param {String/HTMLElement/Element} container The container this layout is bound to
50953 * @param {Object} config Configuration options
50954  */
50955 Roo.BorderLayout = function(container, config){
50956     config = config || {};
50957     Roo.BorderLayout.superclass.constructor.call(this, container, config);
50958     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
50959     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
50960         var target = this.factory.validRegions[i];
50961         if(config[target]){
50962             this.addRegion(target, config[target]);
50963         }
50964     }
50965 };
50966
50967 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
50968     /**
50969      * Creates and adds a new region if it doesn't already exist.
50970      * @param {String} target The target region key (north, south, east, west or center).
50971      * @param {Object} config The regions config object
50972      * @return {BorderLayoutRegion} The new region
50973      */
50974     addRegion : function(target, config){
50975         if(!this.regions[target]){
50976             var r = this.factory.create(target, this, config);
50977             this.bindRegion(target, r);
50978         }
50979         return this.regions[target];
50980     },
50981
50982     // private (kinda)
50983     bindRegion : function(name, r){
50984         this.regions[name] = r;
50985         r.on("visibilitychange", this.layout, this);
50986         r.on("paneladded", this.layout, this);
50987         r.on("panelremoved", this.layout, this);
50988         r.on("invalidated", this.layout, this);
50989         r.on("resized", this.onRegionResized, this);
50990         r.on("collapsed", this.onRegionCollapsed, this);
50991         r.on("expanded", this.onRegionExpanded, this);
50992     },
50993
50994     /**
50995      * Performs a layout update.
50996      */
50997     layout : function(){
50998         if(this.updating) {
50999             return;
51000         }
51001         var size = this.getViewSize();
51002         var w = size.width;
51003         var h = size.height;
51004         var centerW = w;
51005         var centerH = h;
51006         var centerY = 0;
51007         var centerX = 0;
51008         //var x = 0, y = 0;
51009
51010         var rs = this.regions;
51011         var north = rs["north"];
51012         var south = rs["south"]; 
51013         var west = rs["west"];
51014         var east = rs["east"];
51015         var center = rs["center"];
51016         //if(this.hideOnLayout){ // not supported anymore
51017             //c.el.setStyle("display", "none");
51018         //}
51019         if(north && north.isVisible()){
51020             var b = north.getBox();
51021             var m = north.getMargins();
51022             b.width = w - (m.left+m.right);
51023             b.x = m.left;
51024             b.y = m.top;
51025             centerY = b.height + b.y + m.bottom;
51026             centerH -= centerY;
51027             north.updateBox(this.safeBox(b));
51028         }
51029         if(south && south.isVisible()){
51030             var b = south.getBox();
51031             var m = south.getMargins();
51032             b.width = w - (m.left+m.right);
51033             b.x = m.left;
51034             var totalHeight = (b.height + m.top + m.bottom);
51035             b.y = h - totalHeight + m.top;
51036             centerH -= totalHeight;
51037             south.updateBox(this.safeBox(b));
51038         }
51039         if(west && west.isVisible()){
51040             var b = west.getBox();
51041             var m = west.getMargins();
51042             b.height = centerH - (m.top+m.bottom);
51043             b.x = m.left;
51044             b.y = centerY + m.top;
51045             var totalWidth = (b.width + m.left + m.right);
51046             centerX += totalWidth;
51047             centerW -= totalWidth;
51048             west.updateBox(this.safeBox(b));
51049         }
51050         if(east && east.isVisible()){
51051             var b = east.getBox();
51052             var m = east.getMargins();
51053             b.height = centerH - (m.top+m.bottom);
51054             var totalWidth = (b.width + m.left + m.right);
51055             b.x = w - totalWidth + m.left;
51056             b.y = centerY + m.top;
51057             centerW -= totalWidth;
51058             east.updateBox(this.safeBox(b));
51059         }
51060         if(center){
51061             var m = center.getMargins();
51062             var centerBox = {
51063                 x: centerX + m.left,
51064                 y: centerY + m.top,
51065                 width: centerW - (m.left+m.right),
51066                 height: centerH - (m.top+m.bottom)
51067             };
51068             //if(this.hideOnLayout){
51069                 //center.el.setStyle("display", "block");
51070             //}
51071             center.updateBox(this.safeBox(centerBox));
51072         }
51073         this.el.repaint();
51074         this.fireEvent("layout", this);
51075     },
51076
51077     // private
51078     safeBox : function(box){
51079         box.width = Math.max(0, box.width);
51080         box.height = Math.max(0, box.height);
51081         return box;
51082     },
51083
51084     /**
51085      * Adds a ContentPanel (or subclass) to this layout.
51086      * @param {String} target The target region key (north, south, east, west or center).
51087      * @param {Roo.ContentPanel} panel The panel to add
51088      * @return {Roo.ContentPanel} The added panel
51089      */
51090     add : function(target, panel){
51091          
51092         target = target.toLowerCase();
51093         return this.regions[target].add(panel);
51094     },
51095
51096     /**
51097      * Remove a ContentPanel (or subclass) to this layout.
51098      * @param {String} target The target region key (north, south, east, west or center).
51099      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51100      * @return {Roo.ContentPanel} The removed panel
51101      */
51102     remove : function(target, panel){
51103         target = target.toLowerCase();
51104         return this.regions[target].remove(panel);
51105     },
51106
51107     /**
51108      * Searches all regions for a panel with the specified id
51109      * @param {String} panelId
51110      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51111      */
51112     findPanel : function(panelId){
51113         var rs = this.regions;
51114         for(var target in rs){
51115             if(typeof rs[target] != "function"){
51116                 var p = rs[target].getPanel(panelId);
51117                 if(p){
51118                     return p;
51119                 }
51120             }
51121         }
51122         return null;
51123     },
51124
51125     /**
51126      * Searches all regions for a panel with the specified id and activates (shows) it.
51127      * @param {String/ContentPanel} panelId The panels id or the panel itself
51128      * @return {Roo.ContentPanel} The shown panel or null
51129      */
51130     showPanel : function(panelId) {
51131       var rs = this.regions;
51132       for(var target in rs){
51133          var r = rs[target];
51134          if(typeof r != "function"){
51135             if(r.hasPanel(panelId)){
51136                return r.showPanel(panelId);
51137             }
51138          }
51139       }
51140       return null;
51141    },
51142
51143    /**
51144      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51145      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51146      */
51147     restoreState : function(provider){
51148         if(!provider){
51149             provider = Roo.state.Manager;
51150         }
51151         var sm = new Roo.LayoutStateManager();
51152         sm.init(this, provider);
51153     },
51154
51155     /**
51156      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51157      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51158      * a valid ContentPanel config object.  Example:
51159      * <pre><code>
51160 // Create the main layout
51161 var layout = new Roo.BorderLayout('main-ct', {
51162     west: {
51163         split:true,
51164         minSize: 175,
51165         titlebar: true
51166     },
51167     center: {
51168         title:'Components'
51169     }
51170 }, 'main-ct');
51171
51172 // Create and add multiple ContentPanels at once via configs
51173 layout.batchAdd({
51174    west: {
51175        id: 'source-files',
51176        autoCreate:true,
51177        title:'Ext Source Files',
51178        autoScroll:true,
51179        fitToFrame:true
51180    },
51181    center : {
51182        el: cview,
51183        autoScroll:true,
51184        fitToFrame:true,
51185        toolbar: tb,
51186        resizeEl:'cbody'
51187    }
51188 });
51189 </code></pre>
51190      * @param {Object} regions An object containing ContentPanel configs by region name
51191      */
51192     batchAdd : function(regions){
51193         this.beginUpdate();
51194         for(var rname in regions){
51195             var lr = this.regions[rname];
51196             if(lr){
51197                 this.addTypedPanels(lr, regions[rname]);
51198             }
51199         }
51200         this.endUpdate();
51201     },
51202
51203     // private
51204     addTypedPanels : function(lr, ps){
51205         if(typeof ps == 'string'){
51206             lr.add(new Roo.ContentPanel(ps));
51207         }
51208         else if(ps instanceof Array){
51209             for(var i =0, len = ps.length; i < len; i++){
51210                 this.addTypedPanels(lr, ps[i]);
51211             }
51212         }
51213         else if(!ps.events){ // raw config?
51214             var el = ps.el;
51215             delete ps.el; // prevent conflict
51216             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51217         }
51218         else {  // panel object assumed!
51219             lr.add(ps);
51220         }
51221     },
51222     /**
51223      * Adds a xtype elements to the layout.
51224      * <pre><code>
51225
51226 layout.addxtype({
51227        xtype : 'ContentPanel',
51228        region: 'west',
51229        items: [ .... ]
51230    }
51231 );
51232
51233 layout.addxtype({
51234         xtype : 'NestedLayoutPanel',
51235         region: 'west',
51236         layout: {
51237            center: { },
51238            west: { }   
51239         },
51240         items : [ ... list of content panels or nested layout panels.. ]
51241    }
51242 );
51243 </code></pre>
51244      * @param {Object} cfg Xtype definition of item to add.
51245      */
51246     addxtype : function(cfg)
51247     {
51248         // basically accepts a pannel...
51249         // can accept a layout region..!?!?
51250         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51251         
51252         if (!cfg.xtype.match(/Panel$/)) {
51253             return false;
51254         }
51255         var ret = false;
51256         
51257         if (typeof(cfg.region) == 'undefined') {
51258             Roo.log("Failed to add Panel, region was not set");
51259             Roo.log(cfg);
51260             return false;
51261         }
51262         var region = cfg.region;
51263         delete cfg.region;
51264         
51265           
51266         var xitems = [];
51267         if (cfg.items) {
51268             xitems = cfg.items;
51269             delete cfg.items;
51270         }
51271         var nb = false;
51272         
51273         switch(cfg.xtype) 
51274         {
51275             case 'ContentPanel':  // ContentPanel (el, cfg)
51276             case 'ScrollPanel':  // ContentPanel (el, cfg)
51277             case 'ViewPanel': 
51278                 if(cfg.autoCreate) {
51279                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51280                 } else {
51281                     var el = this.el.createChild();
51282                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51283                 }
51284                 
51285                 this.add(region, ret);
51286                 break;
51287             
51288             
51289             case 'TreePanel': // our new panel!
51290                 cfg.el = this.el.createChild();
51291                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51292                 this.add(region, ret);
51293                 break;
51294             
51295             case 'NestedLayoutPanel': 
51296                 // create a new Layout (which is  a Border Layout...
51297                 var el = this.el.createChild();
51298                 var clayout = cfg.layout;
51299                 delete cfg.layout;
51300                 clayout.items   = clayout.items  || [];
51301                 // replace this exitems with the clayout ones..
51302                 xitems = clayout.items;
51303                  
51304                 
51305                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51306                     cfg.background = false;
51307                 }
51308                 var layout = new Roo.BorderLayout(el, clayout);
51309                 
51310                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51311                 //console.log('adding nested layout panel '  + cfg.toSource());
51312                 this.add(region, ret);
51313                 nb = {}; /// find first...
51314                 break;
51315                 
51316             case 'GridPanel': 
51317             
51318                 // needs grid and region
51319                 
51320                 //var el = this.getRegion(region).el.createChild();
51321                 var el = this.el.createChild();
51322                 // create the grid first...
51323                 
51324                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51325                 delete cfg.grid;
51326                 if (region == 'center' && this.active ) {
51327                     cfg.background = false;
51328                 }
51329                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51330                 
51331                 this.add(region, ret);
51332                 if (cfg.background) {
51333                     ret.on('activate', function(gp) {
51334                         if (!gp.grid.rendered) {
51335                             gp.grid.render();
51336                         }
51337                     });
51338                 } else {
51339                     grid.render();
51340                 }
51341                 break;
51342            
51343            
51344            
51345                 
51346                 
51347                 
51348             default:
51349                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51350                     
51351                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51352                     this.add(region, ret);
51353                 } else {
51354                 
51355                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51356                     return null;
51357                 }
51358                 
51359              // GridPanel (grid, cfg)
51360             
51361         }
51362         this.beginUpdate();
51363         // add children..
51364         var region = '';
51365         var abn = {};
51366         Roo.each(xitems, function(i)  {
51367             region = nb && i.region ? i.region : false;
51368             
51369             var add = ret.addxtype(i);
51370            
51371             if (region) {
51372                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51373                 if (!i.background) {
51374                     abn[region] = nb[region] ;
51375                 }
51376             }
51377             
51378         });
51379         this.endUpdate();
51380
51381         // make the last non-background panel active..
51382         //if (nb) { Roo.log(abn); }
51383         if (nb) {
51384             
51385             for(var r in abn) {
51386                 region = this.getRegion(r);
51387                 if (region) {
51388                     // tried using nb[r], but it does not work..
51389                      
51390                     region.showPanel(abn[r]);
51391                    
51392                 }
51393             }
51394         }
51395         return ret;
51396         
51397     }
51398 });
51399
51400 /**
51401  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51402  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51403  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51404  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51405  * <pre><code>
51406 // shorthand
51407 var CP = Roo.ContentPanel;
51408
51409 var layout = Roo.BorderLayout.create({
51410     north: {
51411         initialSize: 25,
51412         titlebar: false,
51413         panels: [new CP("north", "North")]
51414     },
51415     west: {
51416         split:true,
51417         initialSize: 200,
51418         minSize: 175,
51419         maxSize: 400,
51420         titlebar: true,
51421         collapsible: true,
51422         panels: [new CP("west", {title: "West"})]
51423     },
51424     east: {
51425         split:true,
51426         initialSize: 202,
51427         minSize: 175,
51428         maxSize: 400,
51429         titlebar: true,
51430         collapsible: true,
51431         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51432     },
51433     south: {
51434         split:true,
51435         initialSize: 100,
51436         minSize: 100,
51437         maxSize: 200,
51438         titlebar: true,
51439         collapsible: true,
51440         panels: [new CP("south", {title: "South", closable: true})]
51441     },
51442     center: {
51443         titlebar: true,
51444         autoScroll:true,
51445         resizeTabs: true,
51446         minTabWidth: 50,
51447         preferredTabWidth: 150,
51448         panels: [
51449             new CP("center1", {title: "Close Me", closable: true}),
51450             new CP("center2", {title: "Center Panel", closable: false})
51451         ]
51452     }
51453 }, document.body);
51454
51455 layout.getRegion("center").showPanel("center1");
51456 </code></pre>
51457  * @param config
51458  * @param targetEl
51459  */
51460 Roo.BorderLayout.create = function(config, targetEl){
51461     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51462     layout.beginUpdate();
51463     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51464     for(var j = 0, jlen = regions.length; j < jlen; j++){
51465         var lr = regions[j];
51466         if(layout.regions[lr] && config[lr].panels){
51467             var r = layout.regions[lr];
51468             var ps = config[lr].panels;
51469             layout.addTypedPanels(r, ps);
51470         }
51471     }
51472     layout.endUpdate();
51473     return layout;
51474 };
51475
51476 // private
51477 Roo.BorderLayout.RegionFactory = {
51478     // private
51479     validRegions : ["north","south","east","west","center"],
51480
51481     // private
51482     create : function(target, mgr, config){
51483         target = target.toLowerCase();
51484         if(config.lightweight || config.basic){
51485             return new Roo.BasicLayoutRegion(mgr, config, target);
51486         }
51487         switch(target){
51488             case "north":
51489                 return new Roo.NorthLayoutRegion(mgr, config);
51490             case "south":
51491                 return new Roo.SouthLayoutRegion(mgr, config);
51492             case "east":
51493                 return new Roo.EastLayoutRegion(mgr, config);
51494             case "west":
51495                 return new Roo.WestLayoutRegion(mgr, config);
51496             case "center":
51497                 return new Roo.CenterLayoutRegion(mgr, config);
51498         }
51499         throw 'Layout region "'+target+'" not supported.';
51500     }
51501 };/*
51502  * Based on:
51503  * Ext JS Library 1.1.1
51504  * Copyright(c) 2006-2007, Ext JS, LLC.
51505  *
51506  * Originally Released Under LGPL - original licence link has changed is not relivant.
51507  *
51508  * Fork - LGPL
51509  * <script type="text/javascript">
51510  */
51511  
51512 /**
51513  * @class Roo.BasicLayoutRegion
51514  * @extends Roo.util.Observable
51515  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51516  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51517  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51518  */
51519 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51520     this.mgr = mgr;
51521     this.position  = pos;
51522     this.events = {
51523         /**
51524          * @scope Roo.BasicLayoutRegion
51525          */
51526         
51527         /**
51528          * @event beforeremove
51529          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51530          * @param {Roo.LayoutRegion} this
51531          * @param {Roo.ContentPanel} panel The panel
51532          * @param {Object} e The cancel event object
51533          */
51534         "beforeremove" : true,
51535         /**
51536          * @event invalidated
51537          * Fires when the layout for this region is changed.
51538          * @param {Roo.LayoutRegion} this
51539          */
51540         "invalidated" : true,
51541         /**
51542          * @event visibilitychange
51543          * Fires when this region is shown or hidden 
51544          * @param {Roo.LayoutRegion} this
51545          * @param {Boolean} visibility true or false
51546          */
51547         "visibilitychange" : true,
51548         /**
51549          * @event paneladded
51550          * Fires when a panel is added. 
51551          * @param {Roo.LayoutRegion} this
51552          * @param {Roo.ContentPanel} panel The panel
51553          */
51554         "paneladded" : true,
51555         /**
51556          * @event panelremoved
51557          * Fires when a panel is removed. 
51558          * @param {Roo.LayoutRegion} this
51559          * @param {Roo.ContentPanel} panel The panel
51560          */
51561         "panelremoved" : true,
51562         /**
51563          * @event beforecollapse
51564          * Fires when this region before collapse.
51565          * @param {Roo.LayoutRegion} this
51566          */
51567         "beforecollapse" : true,
51568         /**
51569          * @event collapsed
51570          * Fires when this region is collapsed.
51571          * @param {Roo.LayoutRegion} this
51572          */
51573         "collapsed" : true,
51574         /**
51575          * @event expanded
51576          * Fires when this region is expanded.
51577          * @param {Roo.LayoutRegion} this
51578          */
51579         "expanded" : true,
51580         /**
51581          * @event slideshow
51582          * Fires when this region is slid into view.
51583          * @param {Roo.LayoutRegion} this
51584          */
51585         "slideshow" : true,
51586         /**
51587          * @event slidehide
51588          * Fires when this region slides out of view. 
51589          * @param {Roo.LayoutRegion} this
51590          */
51591         "slidehide" : true,
51592         /**
51593          * @event panelactivated
51594          * Fires when a panel is activated. 
51595          * @param {Roo.LayoutRegion} this
51596          * @param {Roo.ContentPanel} panel The activated panel
51597          */
51598         "panelactivated" : true,
51599         /**
51600          * @event resized
51601          * Fires when the user resizes this region. 
51602          * @param {Roo.LayoutRegion} this
51603          * @param {Number} newSize The new size (width for east/west, height for north/south)
51604          */
51605         "resized" : true
51606     };
51607     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51608     this.panels = new Roo.util.MixedCollection();
51609     this.panels.getKey = this.getPanelId.createDelegate(this);
51610     this.box = null;
51611     this.activePanel = null;
51612     // ensure listeners are added...
51613     
51614     if (config.listeners || config.events) {
51615         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51616             listeners : config.listeners || {},
51617             events : config.events || {}
51618         });
51619     }
51620     
51621     if(skipConfig !== true){
51622         this.applyConfig(config);
51623     }
51624 };
51625
51626 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51627     getPanelId : function(p){
51628         return p.getId();
51629     },
51630     
51631     applyConfig : function(config){
51632         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51633         this.config = config;
51634         
51635     },
51636     
51637     /**
51638      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51639      * the width, for horizontal (north, south) the height.
51640      * @param {Number} newSize The new width or height
51641      */
51642     resizeTo : function(newSize){
51643         var el = this.el ? this.el :
51644                  (this.activePanel ? this.activePanel.getEl() : null);
51645         if(el){
51646             switch(this.position){
51647                 case "east":
51648                 case "west":
51649                     el.setWidth(newSize);
51650                     this.fireEvent("resized", this, newSize);
51651                 break;
51652                 case "north":
51653                 case "south":
51654                     el.setHeight(newSize);
51655                     this.fireEvent("resized", this, newSize);
51656                 break;                
51657             }
51658         }
51659     },
51660     
51661     getBox : function(){
51662         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51663     },
51664     
51665     getMargins : function(){
51666         return this.margins;
51667     },
51668     
51669     updateBox : function(box){
51670         this.box = box;
51671         var el = this.activePanel.getEl();
51672         el.dom.style.left = box.x + "px";
51673         el.dom.style.top = box.y + "px";
51674         this.activePanel.setSize(box.width, box.height);
51675     },
51676     
51677     /**
51678      * Returns the container element for this region.
51679      * @return {Roo.Element}
51680      */
51681     getEl : function(){
51682         return this.activePanel;
51683     },
51684     
51685     /**
51686      * Returns true if this region is currently visible.
51687      * @return {Boolean}
51688      */
51689     isVisible : function(){
51690         return this.activePanel ? true : false;
51691     },
51692     
51693     setActivePanel : function(panel){
51694         panel = this.getPanel(panel);
51695         if(this.activePanel && this.activePanel != panel){
51696             this.activePanel.setActiveState(false);
51697             this.activePanel.getEl().setLeftTop(-10000,-10000);
51698         }
51699         this.activePanel = panel;
51700         panel.setActiveState(true);
51701         if(this.box){
51702             panel.setSize(this.box.width, this.box.height);
51703         }
51704         this.fireEvent("panelactivated", this, panel);
51705         this.fireEvent("invalidated");
51706     },
51707     
51708     /**
51709      * Show the specified panel.
51710      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51711      * @return {Roo.ContentPanel} The shown panel or null
51712      */
51713     showPanel : function(panel){
51714         if(panel = this.getPanel(panel)){
51715             this.setActivePanel(panel);
51716         }
51717         return panel;
51718     },
51719     
51720     /**
51721      * Get the active panel for this region.
51722      * @return {Roo.ContentPanel} The active panel or null
51723      */
51724     getActivePanel : function(){
51725         return this.activePanel;
51726     },
51727     
51728     /**
51729      * Add the passed ContentPanel(s)
51730      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51731      * @return {Roo.ContentPanel} The panel added (if only one was added)
51732      */
51733     add : function(panel){
51734         if(arguments.length > 1){
51735             for(var i = 0, len = arguments.length; i < len; i++) {
51736                 this.add(arguments[i]);
51737             }
51738             return null;
51739         }
51740         if(this.hasPanel(panel)){
51741             this.showPanel(panel);
51742             return panel;
51743         }
51744         var el = panel.getEl();
51745         if(el.dom.parentNode != this.mgr.el.dom){
51746             this.mgr.el.dom.appendChild(el.dom);
51747         }
51748         if(panel.setRegion){
51749             panel.setRegion(this);
51750         }
51751         this.panels.add(panel);
51752         el.setStyle("position", "absolute");
51753         if(!panel.background){
51754             this.setActivePanel(panel);
51755             if(this.config.initialSize && this.panels.getCount()==1){
51756                 this.resizeTo(this.config.initialSize);
51757             }
51758         }
51759         this.fireEvent("paneladded", this, panel);
51760         return panel;
51761     },
51762     
51763     /**
51764      * Returns true if the panel is in this region.
51765      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51766      * @return {Boolean}
51767      */
51768     hasPanel : function(panel){
51769         if(typeof panel == "object"){ // must be panel obj
51770             panel = panel.getId();
51771         }
51772         return this.getPanel(panel) ? true : false;
51773     },
51774     
51775     /**
51776      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51777      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51778      * @param {Boolean} preservePanel Overrides the config preservePanel option
51779      * @return {Roo.ContentPanel} The panel that was removed
51780      */
51781     remove : function(panel, preservePanel){
51782         panel = this.getPanel(panel);
51783         if(!panel){
51784             return null;
51785         }
51786         var e = {};
51787         this.fireEvent("beforeremove", this, panel, e);
51788         if(e.cancel === true){
51789             return null;
51790         }
51791         var panelId = panel.getId();
51792         this.panels.removeKey(panelId);
51793         return panel;
51794     },
51795     
51796     /**
51797      * Returns the panel specified or null if it's not in this region.
51798      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51799      * @return {Roo.ContentPanel}
51800      */
51801     getPanel : function(id){
51802         if(typeof id == "object"){ // must be panel obj
51803             return id;
51804         }
51805         return this.panels.get(id);
51806     },
51807     
51808     /**
51809      * Returns this regions position (north/south/east/west/center).
51810      * @return {String} 
51811      */
51812     getPosition: function(){
51813         return this.position;    
51814     }
51815 });/*
51816  * Based on:
51817  * Ext JS Library 1.1.1
51818  * Copyright(c) 2006-2007, Ext JS, LLC.
51819  *
51820  * Originally Released Under LGPL - original licence link has changed is not relivant.
51821  *
51822  * Fork - LGPL
51823  * <script type="text/javascript">
51824  */
51825  
51826 /**
51827  * @class Roo.LayoutRegion
51828  * @extends Roo.BasicLayoutRegion
51829  * This class represents a region in a layout manager.
51830  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51831  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51832  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51833  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51834  * @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})
51835  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51836  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51837  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51838  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51839  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51840  * @cfg {String}    title           The title for the region (overrides panel titles)
51841  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51842  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51843  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51844  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51845  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51846  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51847  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51848  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51849  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51850  * @cfg {Boolean}   showPin         True to show a pin button
51851  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51852  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51853  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51854  * @cfg {Number}    width           For East/West panels
51855  * @cfg {Number}    height          For North/South panels
51856  * @cfg {Boolean}   split           To show the splitter
51857  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51858  */
51859 Roo.LayoutRegion = function(mgr, config, pos){
51860     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51861     var dh = Roo.DomHelper;
51862     /** This region's container element 
51863     * @type Roo.Element */
51864     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51865     /** This region's title element 
51866     * @type Roo.Element */
51867
51868     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51869         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51870         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51871     ]}, true);
51872     this.titleEl.enableDisplayMode();
51873     /** This region's title text element 
51874     * @type HTMLElement */
51875     this.titleTextEl = this.titleEl.dom.firstChild;
51876     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51877     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51878     this.closeBtn.enableDisplayMode();
51879     this.closeBtn.on("click", this.closeClicked, this);
51880     this.closeBtn.hide();
51881
51882     this.createBody(config);
51883     this.visible = true;
51884     this.collapsed = false;
51885
51886     if(config.hideWhenEmpty){
51887         this.hide();
51888         this.on("paneladded", this.validateVisibility, this);
51889         this.on("panelremoved", this.validateVisibility, this);
51890     }
51891     this.applyConfig(config);
51892 };
51893
51894 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51895
51896     createBody : function(){
51897         /** This region's body element 
51898         * @type Roo.Element */
51899         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51900     },
51901
51902     applyConfig : function(c){
51903         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51904             var dh = Roo.DomHelper;
51905             if(c.titlebar !== false){
51906                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51907                 this.collapseBtn.on("click", this.collapse, this);
51908                 this.collapseBtn.enableDisplayMode();
51909
51910                 if(c.showPin === true || this.showPin){
51911                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51912                     this.stickBtn.enableDisplayMode();
51913                     this.stickBtn.on("click", this.expand, this);
51914                     this.stickBtn.hide();
51915                 }
51916             }
51917             /** This region's collapsed element
51918             * @type Roo.Element */
51919             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51920                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51921             ]}, true);
51922             if(c.floatable !== false){
51923                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51924                this.collapsedEl.on("click", this.collapseClick, this);
51925             }
51926
51927             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51928                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51929                    id: "message", unselectable: "on", style:{"float":"left"}});
51930                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51931              }
51932             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51933             this.expandBtn.on("click", this.expand, this);
51934         }
51935         if(this.collapseBtn){
51936             this.collapseBtn.setVisible(c.collapsible == true);
51937         }
51938         this.cmargins = c.cmargins || this.cmargins ||
51939                          (this.position == "west" || this.position == "east" ?
51940                              {top: 0, left: 2, right:2, bottom: 0} :
51941                              {top: 2, left: 0, right:0, bottom: 2});
51942         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51943         this.bottomTabs = c.tabPosition != "top";
51944         this.autoScroll = c.autoScroll || false;
51945         if(this.autoScroll){
51946             this.bodyEl.setStyle("overflow", "auto");
51947         }else{
51948             this.bodyEl.setStyle("overflow", "hidden");
51949         }
51950         //if(c.titlebar !== false){
51951             if((!c.titlebar && !c.title) || c.titlebar === false){
51952                 this.titleEl.hide();
51953             }else{
51954                 this.titleEl.show();
51955                 if(c.title){
51956                     this.titleTextEl.innerHTML = c.title;
51957                 }
51958             }
51959         //}
51960         this.duration = c.duration || .30;
51961         this.slideDuration = c.slideDuration || .45;
51962         this.config = c;
51963         if(c.collapsed){
51964             this.collapse(true);
51965         }
51966         if(c.hidden){
51967             this.hide();
51968         }
51969     },
51970     /**
51971      * Returns true if this region is currently visible.
51972      * @return {Boolean}
51973      */
51974     isVisible : function(){
51975         return this.visible;
51976     },
51977
51978     /**
51979      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
51980      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
51981      */
51982     setCollapsedTitle : function(title){
51983         title = title || "&#160;";
51984         if(this.collapsedTitleTextEl){
51985             this.collapsedTitleTextEl.innerHTML = title;
51986         }
51987     },
51988
51989     getBox : function(){
51990         var b;
51991         if(!this.collapsed){
51992             b = this.el.getBox(false, true);
51993         }else{
51994             b = this.collapsedEl.getBox(false, true);
51995         }
51996         return b;
51997     },
51998
51999     getMargins : function(){
52000         return this.collapsed ? this.cmargins : this.margins;
52001     },
52002
52003     highlight : function(){
52004         this.el.addClass("x-layout-panel-dragover");
52005     },
52006
52007     unhighlight : function(){
52008         this.el.removeClass("x-layout-panel-dragover");
52009     },
52010
52011     updateBox : function(box){
52012         this.box = box;
52013         if(!this.collapsed){
52014             this.el.dom.style.left = box.x + "px";
52015             this.el.dom.style.top = box.y + "px";
52016             this.updateBody(box.width, box.height);
52017         }else{
52018             this.collapsedEl.dom.style.left = box.x + "px";
52019             this.collapsedEl.dom.style.top = box.y + "px";
52020             this.collapsedEl.setSize(box.width, box.height);
52021         }
52022         if(this.tabs){
52023             this.tabs.autoSizeTabs();
52024         }
52025     },
52026
52027     updateBody : function(w, h){
52028         if(w !== null){
52029             this.el.setWidth(w);
52030             w -= this.el.getBorderWidth("rl");
52031             if(this.config.adjustments){
52032                 w += this.config.adjustments[0];
52033             }
52034         }
52035         if(h !== null){
52036             this.el.setHeight(h);
52037             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52038             h -= this.el.getBorderWidth("tb");
52039             if(this.config.adjustments){
52040                 h += this.config.adjustments[1];
52041             }
52042             this.bodyEl.setHeight(h);
52043             if(this.tabs){
52044                 h = this.tabs.syncHeight(h);
52045             }
52046         }
52047         if(this.panelSize){
52048             w = w !== null ? w : this.panelSize.width;
52049             h = h !== null ? h : this.panelSize.height;
52050         }
52051         if(this.activePanel){
52052             var el = this.activePanel.getEl();
52053             w = w !== null ? w : el.getWidth();
52054             h = h !== null ? h : el.getHeight();
52055             this.panelSize = {width: w, height: h};
52056             this.activePanel.setSize(w, h);
52057         }
52058         if(Roo.isIE && this.tabs){
52059             this.tabs.el.repaint();
52060         }
52061     },
52062
52063     /**
52064      * Returns the container element for this region.
52065      * @return {Roo.Element}
52066      */
52067     getEl : function(){
52068         return this.el;
52069     },
52070
52071     /**
52072      * Hides this region.
52073      */
52074     hide : function(){
52075         if(!this.collapsed){
52076             this.el.dom.style.left = "-2000px";
52077             this.el.hide();
52078         }else{
52079             this.collapsedEl.dom.style.left = "-2000px";
52080             this.collapsedEl.hide();
52081         }
52082         this.visible = false;
52083         this.fireEvent("visibilitychange", this, false);
52084     },
52085
52086     /**
52087      * Shows this region if it was previously hidden.
52088      */
52089     show : function(){
52090         if(!this.collapsed){
52091             this.el.show();
52092         }else{
52093             this.collapsedEl.show();
52094         }
52095         this.visible = true;
52096         this.fireEvent("visibilitychange", this, true);
52097     },
52098
52099     closeClicked : function(){
52100         if(this.activePanel){
52101             this.remove(this.activePanel);
52102         }
52103     },
52104
52105     collapseClick : function(e){
52106         if(this.isSlid){
52107            e.stopPropagation();
52108            this.slideIn();
52109         }else{
52110            e.stopPropagation();
52111            this.slideOut();
52112         }
52113     },
52114
52115     /**
52116      * Collapses this region.
52117      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52118      */
52119     collapse : function(skipAnim, skipCheck = false){
52120         if(this.collapsed) {
52121             return;
52122         }
52123         
52124         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52125             
52126             this.collapsed = true;
52127             if(this.split){
52128                 this.split.el.hide();
52129             }
52130             if(this.config.animate && skipAnim !== true){
52131                 this.fireEvent("invalidated", this);
52132                 this.animateCollapse();
52133             }else{
52134                 this.el.setLocation(-20000,-20000);
52135                 this.el.hide();
52136                 this.collapsedEl.show();
52137                 this.fireEvent("collapsed", this);
52138                 this.fireEvent("invalidated", this);
52139             }
52140         }
52141         
52142     },
52143
52144     animateCollapse : function(){
52145         // overridden
52146     },
52147
52148     /**
52149      * Expands this region if it was previously collapsed.
52150      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52151      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52152      */
52153     expand : function(e, skipAnim){
52154         if(e) {
52155             e.stopPropagation();
52156         }
52157         if(!this.collapsed || this.el.hasActiveFx()) {
52158             return;
52159         }
52160         if(this.isSlid){
52161             this.afterSlideIn();
52162             skipAnim = true;
52163         }
52164         this.collapsed = false;
52165         if(this.config.animate && skipAnim !== true){
52166             this.animateExpand();
52167         }else{
52168             this.el.show();
52169             if(this.split){
52170                 this.split.el.show();
52171             }
52172             this.collapsedEl.setLocation(-2000,-2000);
52173             this.collapsedEl.hide();
52174             this.fireEvent("invalidated", this);
52175             this.fireEvent("expanded", this);
52176         }
52177     },
52178
52179     animateExpand : function(){
52180         // overridden
52181     },
52182
52183     initTabs : function()
52184     {
52185         this.bodyEl.setStyle("overflow", "hidden");
52186         var ts = new Roo.TabPanel(
52187                 this.bodyEl.dom,
52188                 {
52189                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52190                     disableTooltips: this.config.disableTabTips,
52191                     toolbar : this.config.toolbar
52192                 }
52193         );
52194         if(this.config.hideTabs){
52195             ts.stripWrap.setDisplayed(false);
52196         }
52197         this.tabs = ts;
52198         ts.resizeTabs = this.config.resizeTabs === true;
52199         ts.minTabWidth = this.config.minTabWidth || 40;
52200         ts.maxTabWidth = this.config.maxTabWidth || 250;
52201         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52202         ts.monitorResize = false;
52203         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52204         ts.bodyEl.addClass('x-layout-tabs-body');
52205         this.panels.each(this.initPanelAsTab, this);
52206     },
52207
52208     initPanelAsTab : function(panel){
52209         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52210                     this.config.closeOnTab && panel.isClosable());
52211         if(panel.tabTip !== undefined){
52212             ti.setTooltip(panel.tabTip);
52213         }
52214         ti.on("activate", function(){
52215               this.setActivePanel(panel);
52216         }, this);
52217         if(this.config.closeOnTab){
52218             ti.on("beforeclose", function(t, e){
52219                 e.cancel = true;
52220                 this.remove(panel);
52221             }, this);
52222         }
52223         return ti;
52224     },
52225
52226     updatePanelTitle : function(panel, title){
52227         if(this.activePanel == panel){
52228             this.updateTitle(title);
52229         }
52230         if(this.tabs){
52231             var ti = this.tabs.getTab(panel.getEl().id);
52232             ti.setText(title);
52233             if(panel.tabTip !== undefined){
52234                 ti.setTooltip(panel.tabTip);
52235             }
52236         }
52237     },
52238
52239     updateTitle : function(title){
52240         if(this.titleTextEl && !this.config.title){
52241             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52242         }
52243     },
52244
52245     setActivePanel : function(panel){
52246         panel = this.getPanel(panel);
52247         if(this.activePanel && this.activePanel != panel){
52248             this.activePanel.setActiveState(false);
52249         }
52250         this.activePanel = panel;
52251         panel.setActiveState(true);
52252         if(this.panelSize){
52253             panel.setSize(this.panelSize.width, this.panelSize.height);
52254         }
52255         if(this.closeBtn){
52256             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52257         }
52258         this.updateTitle(panel.getTitle());
52259         if(this.tabs){
52260             this.fireEvent("invalidated", this);
52261         }
52262         this.fireEvent("panelactivated", this, panel);
52263     },
52264
52265     /**
52266      * Shows the specified panel.
52267      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52268      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52269      */
52270     showPanel : function(panel)
52271     {
52272         panel = this.getPanel(panel);
52273         if(panel){
52274             if(this.tabs){
52275                 var tab = this.tabs.getTab(panel.getEl().id);
52276                 if(tab.isHidden()){
52277                     this.tabs.unhideTab(tab.id);
52278                 }
52279                 tab.activate();
52280             }else{
52281                 this.setActivePanel(panel);
52282             }
52283         }
52284         return panel;
52285     },
52286
52287     /**
52288      * Get the active panel for this region.
52289      * @return {Roo.ContentPanel} The active panel or null
52290      */
52291     getActivePanel : function(){
52292         return this.activePanel;
52293     },
52294
52295     validateVisibility : function(){
52296         if(this.panels.getCount() < 1){
52297             this.updateTitle("&#160;");
52298             this.closeBtn.hide();
52299             this.hide();
52300         }else{
52301             if(!this.isVisible()){
52302                 this.show();
52303             }
52304         }
52305     },
52306
52307     /**
52308      * Adds the passed ContentPanel(s) to this region.
52309      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52310      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52311      */
52312     add : function(panel){
52313         if(arguments.length > 1){
52314             for(var i = 0, len = arguments.length; i < len; i++) {
52315                 this.add(arguments[i]);
52316             }
52317             return null;
52318         }
52319         if(this.hasPanel(panel)){
52320             this.showPanel(panel);
52321             return panel;
52322         }
52323         panel.setRegion(this);
52324         this.panels.add(panel);
52325         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52326             this.bodyEl.dom.appendChild(panel.getEl().dom);
52327             if(panel.background !== true){
52328                 this.setActivePanel(panel);
52329             }
52330             this.fireEvent("paneladded", this, panel);
52331             return panel;
52332         }
52333         if(!this.tabs){
52334             this.initTabs();
52335         }else{
52336             this.initPanelAsTab(panel);
52337         }
52338         if(panel.background !== true){
52339             this.tabs.activate(panel.getEl().id);
52340         }
52341         this.fireEvent("paneladded", this, panel);
52342         return panel;
52343     },
52344
52345     /**
52346      * Hides the tab for the specified panel.
52347      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52348      */
52349     hidePanel : function(panel){
52350         if(this.tabs && (panel = this.getPanel(panel))){
52351             this.tabs.hideTab(panel.getEl().id);
52352         }
52353     },
52354
52355     /**
52356      * Unhides the tab for a previously hidden panel.
52357      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52358      */
52359     unhidePanel : function(panel){
52360         if(this.tabs && (panel = this.getPanel(panel))){
52361             this.tabs.unhideTab(panel.getEl().id);
52362         }
52363     },
52364
52365     clearPanels : function(){
52366         while(this.panels.getCount() > 0){
52367              this.remove(this.panels.first());
52368         }
52369     },
52370
52371     /**
52372      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52373      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52374      * @param {Boolean} preservePanel Overrides the config preservePanel option
52375      * @return {Roo.ContentPanel} The panel that was removed
52376      */
52377     remove : function(panel, preservePanel){
52378         panel = this.getPanel(panel);
52379         if(!panel){
52380             return null;
52381         }
52382         var e = {};
52383         this.fireEvent("beforeremove", this, panel, e);
52384         if(e.cancel === true){
52385             return null;
52386         }
52387         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52388         var panelId = panel.getId();
52389         this.panels.removeKey(panelId);
52390         if(preservePanel){
52391             document.body.appendChild(panel.getEl().dom);
52392         }
52393         if(this.tabs){
52394             this.tabs.removeTab(panel.getEl().id);
52395         }else if (!preservePanel){
52396             this.bodyEl.dom.removeChild(panel.getEl().dom);
52397         }
52398         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52399             var p = this.panels.first();
52400             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52401             tempEl.appendChild(p.getEl().dom);
52402             this.bodyEl.update("");
52403             this.bodyEl.dom.appendChild(p.getEl().dom);
52404             tempEl = null;
52405             this.updateTitle(p.getTitle());
52406             this.tabs = null;
52407             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52408             this.setActivePanel(p);
52409         }
52410         panel.setRegion(null);
52411         if(this.activePanel == panel){
52412             this.activePanel = null;
52413         }
52414         if(this.config.autoDestroy !== false && preservePanel !== true){
52415             try{panel.destroy();}catch(e){}
52416         }
52417         this.fireEvent("panelremoved", this, panel);
52418         return panel;
52419     },
52420
52421     /**
52422      * Returns the TabPanel component used by this region
52423      * @return {Roo.TabPanel}
52424      */
52425     getTabs : function(){
52426         return this.tabs;
52427     },
52428
52429     createTool : function(parentEl, className){
52430         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52431             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52432         btn.addClassOnOver("x-layout-tools-button-over");
52433         return btn;
52434     }
52435 });/*
52436  * Based on:
52437  * Ext JS Library 1.1.1
52438  * Copyright(c) 2006-2007, Ext JS, LLC.
52439  *
52440  * Originally Released Under LGPL - original licence link has changed is not relivant.
52441  *
52442  * Fork - LGPL
52443  * <script type="text/javascript">
52444  */
52445  
52446
52447
52448 /**
52449  * @class Roo.SplitLayoutRegion
52450  * @extends Roo.LayoutRegion
52451  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52452  */
52453 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52454     this.cursor = cursor;
52455     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52456 };
52457
52458 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52459     splitTip : "Drag to resize.",
52460     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52461     useSplitTips : false,
52462
52463     applyConfig : function(config){
52464         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52465         if(config.split){
52466             if(!this.split){
52467                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52468                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52469                 /** The SplitBar for this region 
52470                 * @type Roo.SplitBar */
52471                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52472                 this.split.on("moved", this.onSplitMove, this);
52473                 this.split.useShim = config.useShim === true;
52474                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52475                 if(this.useSplitTips){
52476                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52477                 }
52478                 if(config.collapsible){
52479                     this.split.el.on("dblclick", this.collapse,  this);
52480                 }
52481             }
52482             if(typeof config.minSize != "undefined"){
52483                 this.split.minSize = config.minSize;
52484             }
52485             if(typeof config.maxSize != "undefined"){
52486                 this.split.maxSize = config.maxSize;
52487             }
52488             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52489                 this.hideSplitter();
52490             }
52491         }
52492     },
52493
52494     getHMaxSize : function(){
52495          var cmax = this.config.maxSize || 10000;
52496          var center = this.mgr.getRegion("center");
52497          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52498     },
52499
52500     getVMaxSize : function(){
52501          var cmax = this.config.maxSize || 10000;
52502          var center = this.mgr.getRegion("center");
52503          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52504     },
52505
52506     onSplitMove : function(split, newSize){
52507         this.fireEvent("resized", this, newSize);
52508     },
52509     
52510     /** 
52511      * Returns the {@link Roo.SplitBar} for this region.
52512      * @return {Roo.SplitBar}
52513      */
52514     getSplitBar : function(){
52515         return this.split;
52516     },
52517     
52518     hide : function(){
52519         this.hideSplitter();
52520         Roo.SplitLayoutRegion.superclass.hide.call(this);
52521     },
52522
52523     hideSplitter : function(){
52524         if(this.split){
52525             this.split.el.setLocation(-2000,-2000);
52526             this.split.el.hide();
52527         }
52528     },
52529
52530     show : function(){
52531         if(this.split){
52532             this.split.el.show();
52533         }
52534         Roo.SplitLayoutRegion.superclass.show.call(this);
52535     },
52536     
52537     beforeSlide: function(){
52538         if(Roo.isGecko){// firefox overflow auto bug workaround
52539             this.bodyEl.clip();
52540             if(this.tabs) {
52541                 this.tabs.bodyEl.clip();
52542             }
52543             if(this.activePanel){
52544                 this.activePanel.getEl().clip();
52545                 
52546                 if(this.activePanel.beforeSlide){
52547                     this.activePanel.beforeSlide();
52548                 }
52549             }
52550         }
52551     },
52552     
52553     afterSlide : function(){
52554         if(Roo.isGecko){// firefox overflow auto bug workaround
52555             this.bodyEl.unclip();
52556             if(this.tabs) {
52557                 this.tabs.bodyEl.unclip();
52558             }
52559             if(this.activePanel){
52560                 this.activePanel.getEl().unclip();
52561                 if(this.activePanel.afterSlide){
52562                     this.activePanel.afterSlide();
52563                 }
52564             }
52565         }
52566     },
52567
52568     initAutoHide : function(){
52569         if(this.autoHide !== false){
52570             if(!this.autoHideHd){
52571                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52572                 this.autoHideHd = {
52573                     "mouseout": function(e){
52574                         if(!e.within(this.el, true)){
52575                             st.delay(500);
52576                         }
52577                     },
52578                     "mouseover" : function(e){
52579                         st.cancel();
52580                     },
52581                     scope : this
52582                 };
52583             }
52584             this.el.on(this.autoHideHd);
52585         }
52586     },
52587
52588     clearAutoHide : function(){
52589         if(this.autoHide !== false){
52590             this.el.un("mouseout", this.autoHideHd.mouseout);
52591             this.el.un("mouseover", this.autoHideHd.mouseover);
52592         }
52593     },
52594
52595     clearMonitor : function(){
52596         Roo.get(document).un("click", this.slideInIf, this);
52597     },
52598
52599     // these names are backwards but not changed for compat
52600     slideOut : function(){
52601         if(this.isSlid || this.el.hasActiveFx()){
52602             return;
52603         }
52604         this.isSlid = true;
52605         if(this.collapseBtn){
52606             this.collapseBtn.hide();
52607         }
52608         this.closeBtnState = this.closeBtn.getStyle('display');
52609         this.closeBtn.hide();
52610         if(this.stickBtn){
52611             this.stickBtn.show();
52612         }
52613         this.el.show();
52614         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52615         this.beforeSlide();
52616         this.el.setStyle("z-index", 10001);
52617         this.el.slideIn(this.getSlideAnchor(), {
52618             callback: function(){
52619                 this.afterSlide();
52620                 this.initAutoHide();
52621                 Roo.get(document).on("click", this.slideInIf, this);
52622                 this.fireEvent("slideshow", this);
52623             },
52624             scope: this,
52625             block: true
52626         });
52627     },
52628
52629     afterSlideIn : function(){
52630         this.clearAutoHide();
52631         this.isSlid = false;
52632         this.clearMonitor();
52633         this.el.setStyle("z-index", "");
52634         if(this.collapseBtn){
52635             this.collapseBtn.show();
52636         }
52637         this.closeBtn.setStyle('display', this.closeBtnState);
52638         if(this.stickBtn){
52639             this.stickBtn.hide();
52640         }
52641         this.fireEvent("slidehide", this);
52642     },
52643
52644     slideIn : function(cb){
52645         if(!this.isSlid || this.el.hasActiveFx()){
52646             Roo.callback(cb);
52647             return;
52648         }
52649         this.isSlid = false;
52650         this.beforeSlide();
52651         this.el.slideOut(this.getSlideAnchor(), {
52652             callback: function(){
52653                 this.el.setLeftTop(-10000, -10000);
52654                 this.afterSlide();
52655                 this.afterSlideIn();
52656                 Roo.callback(cb);
52657             },
52658             scope: this,
52659             block: true
52660         });
52661     },
52662     
52663     slideInIf : function(e){
52664         if(!e.within(this.el)){
52665             this.slideIn();
52666         }
52667     },
52668
52669     animateCollapse : function(){
52670         this.beforeSlide();
52671         this.el.setStyle("z-index", 20000);
52672         var anchor = this.getSlideAnchor();
52673         this.el.slideOut(anchor, {
52674             callback : function(){
52675                 this.el.setStyle("z-index", "");
52676                 this.collapsedEl.slideIn(anchor, {duration:.3});
52677                 this.afterSlide();
52678                 this.el.setLocation(-10000,-10000);
52679                 this.el.hide();
52680                 this.fireEvent("collapsed", this);
52681             },
52682             scope: this,
52683             block: true
52684         });
52685     },
52686
52687     animateExpand : function(){
52688         this.beforeSlide();
52689         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52690         this.el.setStyle("z-index", 20000);
52691         this.collapsedEl.hide({
52692             duration:.1
52693         });
52694         this.el.slideIn(this.getSlideAnchor(), {
52695             callback : function(){
52696                 this.el.setStyle("z-index", "");
52697                 this.afterSlide();
52698                 if(this.split){
52699                     this.split.el.show();
52700                 }
52701                 this.fireEvent("invalidated", this);
52702                 this.fireEvent("expanded", this);
52703             },
52704             scope: this,
52705             block: true
52706         });
52707     },
52708
52709     anchors : {
52710         "west" : "left",
52711         "east" : "right",
52712         "north" : "top",
52713         "south" : "bottom"
52714     },
52715
52716     sanchors : {
52717         "west" : "l",
52718         "east" : "r",
52719         "north" : "t",
52720         "south" : "b"
52721     },
52722
52723     canchors : {
52724         "west" : "tl-tr",
52725         "east" : "tr-tl",
52726         "north" : "tl-bl",
52727         "south" : "bl-tl"
52728     },
52729
52730     getAnchor : function(){
52731         return this.anchors[this.position];
52732     },
52733
52734     getCollapseAnchor : function(){
52735         return this.canchors[this.position];
52736     },
52737
52738     getSlideAnchor : function(){
52739         return this.sanchors[this.position];
52740     },
52741
52742     getAlignAdj : function(){
52743         var cm = this.cmargins;
52744         switch(this.position){
52745             case "west":
52746                 return [0, 0];
52747             break;
52748             case "east":
52749                 return [0, 0];
52750             break;
52751             case "north":
52752                 return [0, 0];
52753             break;
52754             case "south":
52755                 return [0, 0];
52756             break;
52757         }
52758     },
52759
52760     getExpandAdj : function(){
52761         var c = this.collapsedEl, cm = this.cmargins;
52762         switch(this.position){
52763             case "west":
52764                 return [-(cm.right+c.getWidth()+cm.left), 0];
52765             break;
52766             case "east":
52767                 return [cm.right+c.getWidth()+cm.left, 0];
52768             break;
52769             case "north":
52770                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52771             break;
52772             case "south":
52773                 return [0, cm.top+cm.bottom+c.getHeight()];
52774             break;
52775         }
52776     }
52777 });/*
52778  * Based on:
52779  * Ext JS Library 1.1.1
52780  * Copyright(c) 2006-2007, Ext JS, LLC.
52781  *
52782  * Originally Released Under LGPL - original licence link has changed is not relivant.
52783  *
52784  * Fork - LGPL
52785  * <script type="text/javascript">
52786  */
52787 /*
52788  * These classes are private internal classes
52789  */
52790 Roo.CenterLayoutRegion = function(mgr, config){
52791     Roo.LayoutRegion.call(this, mgr, config, "center");
52792     this.visible = true;
52793     this.minWidth = config.minWidth || 20;
52794     this.minHeight = config.minHeight || 20;
52795 };
52796
52797 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52798     hide : function(){
52799         // center panel can't be hidden
52800     },
52801     
52802     show : function(){
52803         // center panel can't be hidden
52804     },
52805     
52806     getMinWidth: function(){
52807         return this.minWidth;
52808     },
52809     
52810     getMinHeight: function(){
52811         return this.minHeight;
52812     }
52813 });
52814
52815
52816 Roo.NorthLayoutRegion = function(mgr, config){
52817     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52818     if(this.split){
52819         this.split.placement = Roo.SplitBar.TOP;
52820         this.split.orientation = Roo.SplitBar.VERTICAL;
52821         this.split.el.addClass("x-layout-split-v");
52822     }
52823     var size = config.initialSize || config.height;
52824     if(typeof size != "undefined"){
52825         this.el.setHeight(size);
52826     }
52827 };
52828 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52829     orientation: Roo.SplitBar.VERTICAL,
52830     getBox : function(){
52831         if(this.collapsed){
52832             return this.collapsedEl.getBox();
52833         }
52834         var box = this.el.getBox();
52835         if(this.split){
52836             box.height += this.split.el.getHeight();
52837         }
52838         return box;
52839     },
52840     
52841     updateBox : function(box){
52842         if(this.split && !this.collapsed){
52843             box.height -= this.split.el.getHeight();
52844             this.split.el.setLeft(box.x);
52845             this.split.el.setTop(box.y+box.height);
52846             this.split.el.setWidth(box.width);
52847         }
52848         if(this.collapsed){
52849             this.updateBody(box.width, null);
52850         }
52851         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52852     }
52853 });
52854
52855 Roo.SouthLayoutRegion = function(mgr, config){
52856     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52857     if(this.split){
52858         this.split.placement = Roo.SplitBar.BOTTOM;
52859         this.split.orientation = Roo.SplitBar.VERTICAL;
52860         this.split.el.addClass("x-layout-split-v");
52861     }
52862     var size = config.initialSize || config.height;
52863     if(typeof size != "undefined"){
52864         this.el.setHeight(size);
52865     }
52866 };
52867 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52868     orientation: Roo.SplitBar.VERTICAL,
52869     getBox : function(){
52870         if(this.collapsed){
52871             return this.collapsedEl.getBox();
52872         }
52873         var box = this.el.getBox();
52874         if(this.split){
52875             var sh = this.split.el.getHeight();
52876             box.height += sh;
52877             box.y -= sh;
52878         }
52879         return box;
52880     },
52881     
52882     updateBox : function(box){
52883         if(this.split && !this.collapsed){
52884             var sh = this.split.el.getHeight();
52885             box.height -= sh;
52886             box.y += sh;
52887             this.split.el.setLeft(box.x);
52888             this.split.el.setTop(box.y-sh);
52889             this.split.el.setWidth(box.width);
52890         }
52891         if(this.collapsed){
52892             this.updateBody(box.width, null);
52893         }
52894         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52895     }
52896 });
52897
52898 Roo.EastLayoutRegion = function(mgr, config){
52899     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52900     if(this.split){
52901         this.split.placement = Roo.SplitBar.RIGHT;
52902         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52903         this.split.el.addClass("x-layout-split-h");
52904     }
52905     var size = config.initialSize || config.width;
52906     if(typeof size != "undefined"){
52907         this.el.setWidth(size);
52908     }
52909 };
52910 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52911     orientation: Roo.SplitBar.HORIZONTAL,
52912     getBox : function(){
52913         if(this.collapsed){
52914             return this.collapsedEl.getBox();
52915         }
52916         var box = this.el.getBox();
52917         if(this.split){
52918             var sw = this.split.el.getWidth();
52919             box.width += sw;
52920             box.x -= sw;
52921         }
52922         return box;
52923     },
52924
52925     updateBox : function(box){
52926         if(this.split && !this.collapsed){
52927             var sw = this.split.el.getWidth();
52928             box.width -= sw;
52929             this.split.el.setLeft(box.x);
52930             this.split.el.setTop(box.y);
52931             this.split.el.setHeight(box.height);
52932             box.x += sw;
52933         }
52934         if(this.collapsed){
52935             this.updateBody(null, box.height);
52936         }
52937         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52938     }
52939 });
52940
52941 Roo.WestLayoutRegion = function(mgr, config){
52942     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52943     if(this.split){
52944         this.split.placement = Roo.SplitBar.LEFT;
52945         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52946         this.split.el.addClass("x-layout-split-h");
52947     }
52948     var size = config.initialSize || config.width;
52949     if(typeof size != "undefined"){
52950         this.el.setWidth(size);
52951     }
52952 };
52953 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52954     orientation: Roo.SplitBar.HORIZONTAL,
52955     getBox : function(){
52956         if(this.collapsed){
52957             return this.collapsedEl.getBox();
52958         }
52959         var box = this.el.getBox();
52960         if(this.split){
52961             box.width += this.split.el.getWidth();
52962         }
52963         return box;
52964     },
52965     
52966     updateBox : function(box){
52967         if(this.split && !this.collapsed){
52968             var sw = this.split.el.getWidth();
52969             box.width -= sw;
52970             this.split.el.setLeft(box.x+box.width);
52971             this.split.el.setTop(box.y);
52972             this.split.el.setHeight(box.height);
52973         }
52974         if(this.collapsed){
52975             this.updateBody(null, box.height);
52976         }
52977         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52978     }
52979 });
52980 /*
52981  * Based on:
52982  * Ext JS Library 1.1.1
52983  * Copyright(c) 2006-2007, Ext JS, LLC.
52984  *
52985  * Originally Released Under LGPL - original licence link has changed is not relivant.
52986  *
52987  * Fork - LGPL
52988  * <script type="text/javascript">
52989  */
52990  
52991  
52992 /*
52993  * Private internal class for reading and applying state
52994  */
52995 Roo.LayoutStateManager = function(layout){
52996      // default empty state
52997      this.state = {
52998         north: {},
52999         south: {},
53000         east: {},
53001         west: {}       
53002     };
53003 };
53004
53005 Roo.LayoutStateManager.prototype = {
53006     init : function(layout, provider){
53007         this.provider = provider;
53008         var state = provider.get(layout.id+"-layout-state");
53009         if(state){
53010             var wasUpdating = layout.isUpdating();
53011             if(!wasUpdating){
53012                 layout.beginUpdate();
53013             }
53014             for(var key in state){
53015                 if(typeof state[key] != "function"){
53016                     var rstate = state[key];
53017                     var r = layout.getRegion(key);
53018                     if(r && rstate){
53019                         if(rstate.size){
53020                             r.resizeTo(rstate.size);
53021                         }
53022                         if(rstate.collapsed == true){
53023                             r.collapse(true);
53024                         }else{
53025                             r.expand(null, true);
53026                         }
53027                     }
53028                 }
53029             }
53030             if(!wasUpdating){
53031                 layout.endUpdate();
53032             }
53033             this.state = state; 
53034         }
53035         this.layout = layout;
53036         layout.on("regionresized", this.onRegionResized, this);
53037         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53038         layout.on("regionexpanded", this.onRegionExpanded, this);
53039     },
53040     
53041     storeState : function(){
53042         this.provider.set(this.layout.id+"-layout-state", this.state);
53043     },
53044     
53045     onRegionResized : function(region, newSize){
53046         this.state[region.getPosition()].size = newSize;
53047         this.storeState();
53048     },
53049     
53050     onRegionCollapsed : function(region){
53051         this.state[region.getPosition()].collapsed = true;
53052         this.storeState();
53053     },
53054     
53055     onRegionExpanded : function(region){
53056         this.state[region.getPosition()].collapsed = false;
53057         this.storeState();
53058     }
53059 };/*
53060  * Based on:
53061  * Ext JS Library 1.1.1
53062  * Copyright(c) 2006-2007, Ext JS, LLC.
53063  *
53064  * Originally Released Under LGPL - original licence link has changed is not relivant.
53065  *
53066  * Fork - LGPL
53067  * <script type="text/javascript">
53068  */
53069 /**
53070  * @class Roo.ContentPanel
53071  * @extends Roo.util.Observable
53072  * A basic ContentPanel element.
53073  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53074  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53075  * @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
53076  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53077  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53078  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53079  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53080  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53081  * @cfg {String} title          The title for this panel
53082  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53083  * @cfg {String} url            Calls {@link #setUrl} with this value
53084  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53085  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53086  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53087  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53088
53089  * @constructor
53090  * Create a new ContentPanel.
53091  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53092  * @param {String/Object} config A string to set only the title or a config object
53093  * @param {String} content (optional) Set the HTML content for this panel
53094  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53095  */
53096 Roo.ContentPanel = function(el, config, content){
53097     
53098      
53099     /*
53100     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53101         config = el;
53102         el = Roo.id();
53103     }
53104     if (config && config.parentLayout) { 
53105         el = config.parentLayout.el.createChild(); 
53106     }
53107     */
53108     if(el.autoCreate){ // xtype is available if this is called from factory
53109         config = el;
53110         el = Roo.id();
53111     }
53112     this.el = Roo.get(el);
53113     if(!this.el && config && config.autoCreate){
53114         if(typeof config.autoCreate == "object"){
53115             if(!config.autoCreate.id){
53116                 config.autoCreate.id = config.id||el;
53117             }
53118             this.el = Roo.DomHelper.append(document.body,
53119                         config.autoCreate, true);
53120         }else{
53121             this.el = Roo.DomHelper.append(document.body,
53122                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53123         }
53124     }
53125     this.closable = false;
53126     this.loaded = false;
53127     this.active = false;
53128     if(typeof config == "string"){
53129         this.title = config;
53130     }else{
53131         Roo.apply(this, config);
53132     }
53133     
53134     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53135         this.wrapEl = this.el.wrap();
53136         this.toolbar.container = this.el.insertSibling(false, 'before');
53137         this.toolbar = new Roo.Toolbar(this.toolbar);
53138     }
53139     
53140     // xtype created footer. - not sure if will work as we normally have to render first..
53141     if (this.footer && !this.footer.el && this.footer.xtype) {
53142         if (!this.wrapEl) {
53143             this.wrapEl = this.el.wrap();
53144         }
53145     
53146         this.footer.container = this.wrapEl.createChild();
53147          
53148         this.footer = Roo.factory(this.footer, Roo);
53149         
53150     }
53151     
53152     if(this.resizeEl){
53153         this.resizeEl = Roo.get(this.resizeEl, true);
53154     }else{
53155         this.resizeEl = this.el;
53156     }
53157     // handle view.xtype
53158     
53159  
53160     
53161     
53162     this.addEvents({
53163         /**
53164          * @event activate
53165          * Fires when this panel is activated. 
53166          * @param {Roo.ContentPanel} this
53167          */
53168         "activate" : true,
53169         /**
53170          * @event deactivate
53171          * Fires when this panel is activated. 
53172          * @param {Roo.ContentPanel} this
53173          */
53174         "deactivate" : true,
53175
53176         /**
53177          * @event resize
53178          * Fires when this panel is resized if fitToFrame is true.
53179          * @param {Roo.ContentPanel} this
53180          * @param {Number} width The width after any component adjustments
53181          * @param {Number} height The height after any component adjustments
53182          */
53183         "resize" : true,
53184         
53185          /**
53186          * @event render
53187          * Fires when this tab is created
53188          * @param {Roo.ContentPanel} this
53189          */
53190         "render" : true
53191         
53192         
53193         
53194     });
53195     
53196
53197     
53198     
53199     if(this.autoScroll){
53200         this.resizeEl.setStyle("overflow", "auto");
53201     } else {
53202         // fix randome scrolling
53203         this.el.on('scroll', function() {
53204             Roo.log('fix random scolling');
53205             this.scrollTo('top',0); 
53206         });
53207     }
53208     content = content || this.content;
53209     if(content){
53210         this.setContent(content);
53211     }
53212     if(config && config.url){
53213         this.setUrl(this.url, this.params, this.loadOnce);
53214     }
53215     
53216     
53217     
53218     Roo.ContentPanel.superclass.constructor.call(this);
53219     
53220     if (this.view && typeof(this.view.xtype) != 'undefined') {
53221         this.view.el = this.el.appendChild(document.createElement("div"));
53222         this.view = Roo.factory(this.view); 
53223         this.view.render  &&  this.view.render(false, '');  
53224     }
53225     
53226     
53227     this.fireEvent('render', this);
53228 };
53229
53230 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53231     tabTip:'',
53232     setRegion : function(region){
53233         this.region = region;
53234         if(region){
53235            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53236         }else{
53237            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53238         } 
53239     },
53240     
53241     /**
53242      * Returns the toolbar for this Panel if one was configured. 
53243      * @return {Roo.Toolbar} 
53244      */
53245     getToolbar : function(){
53246         return this.toolbar;
53247     },
53248     
53249     setActiveState : function(active){
53250         this.active = active;
53251         if(!active){
53252             this.fireEvent("deactivate", this);
53253         }else{
53254             this.fireEvent("activate", this);
53255         }
53256     },
53257     /**
53258      * Updates this panel's element
53259      * @param {String} content The new content
53260      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53261     */
53262     setContent : function(content, loadScripts){
53263         this.el.update(content, loadScripts);
53264     },
53265
53266     ignoreResize : function(w, h){
53267         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53268             return true;
53269         }else{
53270             this.lastSize = {width: w, height: h};
53271             return false;
53272         }
53273     },
53274     /**
53275      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53276      * @return {Roo.UpdateManager} The UpdateManager
53277      */
53278     getUpdateManager : function(){
53279         return this.el.getUpdateManager();
53280     },
53281      /**
53282      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53283      * @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:
53284 <pre><code>
53285 panel.load({
53286     url: "your-url.php",
53287     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53288     callback: yourFunction,
53289     scope: yourObject, //(optional scope)
53290     discardUrl: false,
53291     nocache: false,
53292     text: "Loading...",
53293     timeout: 30,
53294     scripts: false
53295 });
53296 </code></pre>
53297      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53298      * 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.
53299      * @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}
53300      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53301      * @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.
53302      * @return {Roo.ContentPanel} this
53303      */
53304     load : function(){
53305         var um = this.el.getUpdateManager();
53306         um.update.apply(um, arguments);
53307         return this;
53308     },
53309
53310
53311     /**
53312      * 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.
53313      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53314      * @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)
53315      * @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)
53316      * @return {Roo.UpdateManager} The UpdateManager
53317      */
53318     setUrl : function(url, params, loadOnce){
53319         if(this.refreshDelegate){
53320             this.removeListener("activate", this.refreshDelegate);
53321         }
53322         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53323         this.on("activate", this.refreshDelegate);
53324         return this.el.getUpdateManager();
53325     },
53326     
53327     _handleRefresh : function(url, params, loadOnce){
53328         if(!loadOnce || !this.loaded){
53329             var updater = this.el.getUpdateManager();
53330             updater.update(url, params, this._setLoaded.createDelegate(this));
53331         }
53332     },
53333     
53334     _setLoaded : function(){
53335         this.loaded = true;
53336     }, 
53337     
53338     /**
53339      * Returns this panel's id
53340      * @return {String} 
53341      */
53342     getId : function(){
53343         return this.el.id;
53344     },
53345     
53346     /** 
53347      * Returns this panel's element - used by regiosn to add.
53348      * @return {Roo.Element} 
53349      */
53350     getEl : function(){
53351         return this.wrapEl || this.el;
53352     },
53353     
53354     adjustForComponents : function(width, height)
53355     {
53356         //Roo.log('adjustForComponents ');
53357         if(this.resizeEl != this.el){
53358             width -= this.el.getFrameWidth('lr');
53359             height -= this.el.getFrameWidth('tb');
53360         }
53361         if(this.toolbar){
53362             var te = this.toolbar.getEl();
53363             height -= te.getHeight();
53364             te.setWidth(width);
53365         }
53366         if(this.footer){
53367             var te = this.footer.getEl();
53368             Roo.log("footer:" + te.getHeight());
53369             
53370             height -= te.getHeight();
53371             te.setWidth(width);
53372         }
53373         
53374         
53375         if(this.adjustments){
53376             width += this.adjustments[0];
53377             height += this.adjustments[1];
53378         }
53379         return {"width": width, "height": height};
53380     },
53381     
53382     setSize : function(width, height){
53383         if(this.fitToFrame && !this.ignoreResize(width, height)){
53384             if(this.fitContainer && this.resizeEl != this.el){
53385                 this.el.setSize(width, height);
53386             }
53387             var size = this.adjustForComponents(width, height);
53388             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53389             this.fireEvent('resize', this, size.width, size.height);
53390         }
53391     },
53392     
53393     /**
53394      * Returns this panel's title
53395      * @return {String} 
53396      */
53397     getTitle : function(){
53398         return this.title;
53399     },
53400     
53401     /**
53402      * Set this panel's title
53403      * @param {String} title
53404      */
53405     setTitle : function(title){
53406         this.title = title;
53407         if(this.region){
53408             this.region.updatePanelTitle(this, title);
53409         }
53410     },
53411     
53412     /**
53413      * Returns true is this panel was configured to be closable
53414      * @return {Boolean} 
53415      */
53416     isClosable : function(){
53417         return this.closable;
53418     },
53419     
53420     beforeSlide : function(){
53421         this.el.clip();
53422         this.resizeEl.clip();
53423     },
53424     
53425     afterSlide : function(){
53426         this.el.unclip();
53427         this.resizeEl.unclip();
53428     },
53429     
53430     /**
53431      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53432      *   Will fail silently if the {@link #setUrl} method has not been called.
53433      *   This does not activate the panel, just updates its content.
53434      */
53435     refresh : function(){
53436         if(this.refreshDelegate){
53437            this.loaded = false;
53438            this.refreshDelegate();
53439         }
53440     },
53441     
53442     /**
53443      * Destroys this panel
53444      */
53445     destroy : function(){
53446         this.el.removeAllListeners();
53447         var tempEl = document.createElement("span");
53448         tempEl.appendChild(this.el.dom);
53449         tempEl.innerHTML = "";
53450         this.el.remove();
53451         this.el = null;
53452     },
53453     
53454     /**
53455      * form - if the content panel contains a form - this is a reference to it.
53456      * @type {Roo.form.Form}
53457      */
53458     form : false,
53459     /**
53460      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53461      *    This contains a reference to it.
53462      * @type {Roo.View}
53463      */
53464     view : false,
53465     
53466       /**
53467      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53468      * <pre><code>
53469
53470 layout.addxtype({
53471        xtype : 'Form',
53472        items: [ .... ]
53473    }
53474 );
53475
53476 </code></pre>
53477      * @param {Object} cfg Xtype definition of item to add.
53478      */
53479     
53480     addxtype : function(cfg) {
53481         // add form..
53482         if (cfg.xtype.match(/^Form$/)) {
53483             
53484             var el;
53485             //if (this.footer) {
53486             //    el = this.footer.container.insertSibling(false, 'before');
53487             //} else {
53488                 el = this.el.createChild();
53489             //}
53490
53491             this.form = new  Roo.form.Form(cfg);
53492             
53493             
53494             if ( this.form.allItems.length) {
53495                 this.form.render(el.dom);
53496             }
53497             return this.form;
53498         }
53499         // should only have one of theses..
53500         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53501             // views.. should not be just added - used named prop 'view''
53502             
53503             cfg.el = this.el.appendChild(document.createElement("div"));
53504             // factory?
53505             
53506             var ret = new Roo.factory(cfg);
53507              
53508              ret.render && ret.render(false, ''); // render blank..
53509             this.view = ret;
53510             return ret;
53511         }
53512         return false;
53513     }
53514 });
53515
53516 /**
53517  * @class Roo.GridPanel
53518  * @extends Roo.ContentPanel
53519  * @constructor
53520  * Create a new GridPanel.
53521  * @param {Roo.grid.Grid} grid The grid for this panel
53522  * @param {String/Object} config A string to set only the panel's title, or a config object
53523  */
53524 Roo.GridPanel = function(grid, config){
53525     
53526   
53527     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53528         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53529         
53530     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53531     
53532     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53533     
53534     if(this.toolbar){
53535         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53536     }
53537     // xtype created footer. - not sure if will work as we normally have to render first..
53538     if (this.footer && !this.footer.el && this.footer.xtype) {
53539         
53540         this.footer.container = this.grid.getView().getFooterPanel(true);
53541         this.footer.dataSource = this.grid.dataSource;
53542         this.footer = Roo.factory(this.footer, Roo);
53543         
53544     }
53545     
53546     grid.monitorWindowResize = false; // turn off autosizing
53547     grid.autoHeight = false;
53548     grid.autoWidth = false;
53549     this.grid = grid;
53550     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53551 };
53552
53553 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53554     getId : function(){
53555         return this.grid.id;
53556     },
53557     
53558     /**
53559      * Returns the grid for this panel
53560      * @return {Roo.grid.Grid} 
53561      */
53562     getGrid : function(){
53563         return this.grid;    
53564     },
53565     
53566     setSize : function(width, height){
53567         if(!this.ignoreResize(width, height)){
53568             var grid = this.grid;
53569             var size = this.adjustForComponents(width, height);
53570             grid.getGridEl().setSize(size.width, size.height);
53571             grid.autoSize();
53572         }
53573     },
53574     
53575     beforeSlide : function(){
53576         this.grid.getView().scroller.clip();
53577     },
53578     
53579     afterSlide : function(){
53580         this.grid.getView().scroller.unclip();
53581     },
53582     
53583     destroy : function(){
53584         this.grid.destroy();
53585         delete this.grid;
53586         Roo.GridPanel.superclass.destroy.call(this); 
53587     }
53588 });
53589
53590
53591 /**
53592  * @class Roo.NestedLayoutPanel
53593  * @extends Roo.ContentPanel
53594  * @constructor
53595  * Create a new NestedLayoutPanel.
53596  * 
53597  * 
53598  * @param {Roo.BorderLayout} layout The layout for this panel
53599  * @param {String/Object} config A string to set only the title or a config object
53600  */
53601 Roo.NestedLayoutPanel = function(layout, config)
53602 {
53603     // construct with only one argument..
53604     /* FIXME - implement nicer consturctors
53605     if (layout.layout) {
53606         config = layout;
53607         layout = config.layout;
53608         delete config.layout;
53609     }
53610     if (layout.xtype && !layout.getEl) {
53611         // then layout needs constructing..
53612         layout = Roo.factory(layout, Roo);
53613     }
53614     */
53615     
53616     
53617     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53618     
53619     layout.monitorWindowResize = false; // turn off autosizing
53620     this.layout = layout;
53621     this.layout.getEl().addClass("x-layout-nested-layout");
53622     
53623     
53624     
53625     
53626 };
53627
53628 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53629
53630     setSize : function(width, height){
53631         if(!this.ignoreResize(width, height)){
53632             var size = this.adjustForComponents(width, height);
53633             var el = this.layout.getEl();
53634             el.setSize(size.width, size.height);
53635             var touch = el.dom.offsetWidth;
53636             this.layout.layout();
53637             // ie requires a double layout on the first pass
53638             if(Roo.isIE && !this.initialized){
53639                 this.initialized = true;
53640                 this.layout.layout();
53641             }
53642         }
53643     },
53644     
53645     // activate all subpanels if not currently active..
53646     
53647     setActiveState : function(active){
53648         this.active = active;
53649         if(!active){
53650             this.fireEvent("deactivate", this);
53651             return;
53652         }
53653         
53654         this.fireEvent("activate", this);
53655         // not sure if this should happen before or after..
53656         if (!this.layout) {
53657             return; // should not happen..
53658         }
53659         var reg = false;
53660         for (var r in this.layout.regions) {
53661             reg = this.layout.getRegion(r);
53662             if (reg.getActivePanel()) {
53663                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53664                 reg.setActivePanel(reg.getActivePanel());
53665                 continue;
53666             }
53667             if (!reg.panels.length) {
53668                 continue;
53669             }
53670             reg.showPanel(reg.getPanel(0));
53671         }
53672         
53673         
53674         
53675         
53676     },
53677     
53678     /**
53679      * Returns the nested BorderLayout for this panel
53680      * @return {Roo.BorderLayout} 
53681      */
53682     getLayout : function(){
53683         return this.layout;
53684     },
53685     
53686      /**
53687      * Adds a xtype elements to the layout of the nested panel
53688      * <pre><code>
53689
53690 panel.addxtype({
53691        xtype : 'ContentPanel',
53692        region: 'west',
53693        items: [ .... ]
53694    }
53695 );
53696
53697 panel.addxtype({
53698         xtype : 'NestedLayoutPanel',
53699         region: 'west',
53700         layout: {
53701            center: { },
53702            west: { }   
53703         },
53704         items : [ ... list of content panels or nested layout panels.. ]
53705    }
53706 );
53707 </code></pre>
53708      * @param {Object} cfg Xtype definition of item to add.
53709      */
53710     addxtype : function(cfg) {
53711         return this.layout.addxtype(cfg);
53712     
53713     }
53714 });
53715
53716 Roo.ScrollPanel = function(el, config, content){
53717     config = config || {};
53718     config.fitToFrame = true;
53719     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53720     
53721     this.el.dom.style.overflow = "hidden";
53722     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53723     this.el.removeClass("x-layout-inactive-content");
53724     this.el.on("mousewheel", this.onWheel, this);
53725
53726     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53727     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53728     up.unselectable(); down.unselectable();
53729     up.on("click", this.scrollUp, this);
53730     down.on("click", this.scrollDown, this);
53731     up.addClassOnOver("x-scroller-btn-over");
53732     down.addClassOnOver("x-scroller-btn-over");
53733     up.addClassOnClick("x-scroller-btn-click");
53734     down.addClassOnClick("x-scroller-btn-click");
53735     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53736
53737     this.resizeEl = this.el;
53738     this.el = wrap; this.up = up; this.down = down;
53739 };
53740
53741 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53742     increment : 100,
53743     wheelIncrement : 5,
53744     scrollUp : function(){
53745         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53746     },
53747
53748     scrollDown : function(){
53749         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53750     },
53751
53752     afterScroll : function(){
53753         var el = this.resizeEl;
53754         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53755         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53756         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53757     },
53758
53759     setSize : function(){
53760         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53761         this.afterScroll();
53762     },
53763
53764     onWheel : function(e){
53765         var d = e.getWheelDelta();
53766         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53767         this.afterScroll();
53768         e.stopEvent();
53769     },
53770
53771     setContent : function(content, loadScripts){
53772         this.resizeEl.update(content, loadScripts);
53773     }
53774
53775 });
53776
53777
53778
53779
53780
53781
53782
53783
53784
53785 /**
53786  * @class Roo.TreePanel
53787  * @extends Roo.ContentPanel
53788  * @constructor
53789  * Create a new TreePanel. - defaults to fit/scoll contents.
53790  * @param {String/Object} config A string to set only the panel's title, or a config object
53791  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53792  */
53793 Roo.TreePanel = function(config){
53794     var el = config.el;
53795     var tree = config.tree;
53796     delete config.tree; 
53797     delete config.el; // hopefull!
53798     
53799     // wrapper for IE7 strict & safari scroll issue
53800     
53801     var treeEl = el.createChild();
53802     config.resizeEl = treeEl;
53803     
53804     
53805     
53806     Roo.TreePanel.superclass.constructor.call(this, el, config);
53807  
53808  
53809     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53810     //console.log(tree);
53811     this.on('activate', function()
53812     {
53813         if (this.tree.rendered) {
53814             return;
53815         }
53816         //console.log('render tree');
53817         this.tree.render();
53818     });
53819     // this should not be needed.. - it's actually the 'el' that resizes?
53820     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53821     
53822     //this.on('resize',  function (cp, w, h) {
53823     //        this.tree.innerCt.setWidth(w);
53824     //        this.tree.innerCt.setHeight(h);
53825     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53826     //});
53827
53828         
53829     
53830 };
53831
53832 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53833     fitToFrame : true,
53834     autoScroll : true
53835 });
53836
53837
53838
53839
53840
53841
53842
53843
53844
53845
53846
53847 /*
53848  * Based on:
53849  * Ext JS Library 1.1.1
53850  * Copyright(c) 2006-2007, Ext JS, LLC.
53851  *
53852  * Originally Released Under LGPL - original licence link has changed is not relivant.
53853  *
53854  * Fork - LGPL
53855  * <script type="text/javascript">
53856  */
53857  
53858
53859 /**
53860  * @class Roo.ReaderLayout
53861  * @extends Roo.BorderLayout
53862  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53863  * center region containing two nested regions (a top one for a list view and one for item preview below),
53864  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53865  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53866  * expedites the setup of the overall layout and regions for this common application style.
53867  * Example:
53868  <pre><code>
53869 var reader = new Roo.ReaderLayout();
53870 var CP = Roo.ContentPanel;  // shortcut for adding
53871
53872 reader.beginUpdate();
53873 reader.add("north", new CP("north", "North"));
53874 reader.add("west", new CP("west", {title: "West"}));
53875 reader.add("east", new CP("east", {title: "East"}));
53876
53877 reader.regions.listView.add(new CP("listView", "List"));
53878 reader.regions.preview.add(new CP("preview", "Preview"));
53879 reader.endUpdate();
53880 </code></pre>
53881 * @constructor
53882 * Create a new ReaderLayout
53883 * @param {Object} config Configuration options
53884 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53885 * document.body if omitted)
53886 */
53887 Roo.ReaderLayout = function(config, renderTo){
53888     var c = config || {size:{}};
53889     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53890         north: c.north !== false ? Roo.apply({
53891             split:false,
53892             initialSize: 32,
53893             titlebar: false
53894         }, c.north) : false,
53895         west: c.west !== false ? Roo.apply({
53896             split:true,
53897             initialSize: 200,
53898             minSize: 175,
53899             maxSize: 400,
53900             titlebar: true,
53901             collapsible: true,
53902             animate: true,
53903             margins:{left:5,right:0,bottom:5,top:5},
53904             cmargins:{left:5,right:5,bottom:5,top:5}
53905         }, c.west) : false,
53906         east: c.east !== false ? Roo.apply({
53907             split:true,
53908             initialSize: 200,
53909             minSize: 175,
53910             maxSize: 400,
53911             titlebar: true,
53912             collapsible: true,
53913             animate: true,
53914             margins:{left:0,right:5,bottom:5,top:5},
53915             cmargins:{left:5,right:5,bottom:5,top:5}
53916         }, c.east) : false,
53917         center: Roo.apply({
53918             tabPosition: 'top',
53919             autoScroll:false,
53920             closeOnTab: true,
53921             titlebar:false,
53922             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53923         }, c.center)
53924     });
53925
53926     this.el.addClass('x-reader');
53927
53928     this.beginUpdate();
53929
53930     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53931         south: c.preview !== false ? Roo.apply({
53932             split:true,
53933             initialSize: 200,
53934             minSize: 100,
53935             autoScroll:true,
53936             collapsible:true,
53937             titlebar: true,
53938             cmargins:{top:5,left:0, right:0, bottom:0}
53939         }, c.preview) : false,
53940         center: Roo.apply({
53941             autoScroll:false,
53942             titlebar:false,
53943             minHeight:200
53944         }, c.listView)
53945     });
53946     this.add('center', new Roo.NestedLayoutPanel(inner,
53947             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53948
53949     this.endUpdate();
53950
53951     this.regions.preview = inner.getRegion('south');
53952     this.regions.listView = inner.getRegion('center');
53953 };
53954
53955 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53956  * Based on:
53957  * Ext JS Library 1.1.1
53958  * Copyright(c) 2006-2007, Ext JS, LLC.
53959  *
53960  * Originally Released Under LGPL - original licence link has changed is not relivant.
53961  *
53962  * Fork - LGPL
53963  * <script type="text/javascript">
53964  */
53965  
53966 /**
53967  * @class Roo.grid.Grid
53968  * @extends Roo.util.Observable
53969  * This class represents the primary interface of a component based grid control.
53970  * <br><br>Usage:<pre><code>
53971  var grid = new Roo.grid.Grid("my-container-id", {
53972      ds: myDataStore,
53973      cm: myColModel,
53974      selModel: mySelectionModel,
53975      autoSizeColumns: true,
53976      monitorWindowResize: false,
53977      trackMouseOver: true
53978  });
53979  // set any options
53980  grid.render();
53981  * </code></pre>
53982  * <b>Common Problems:</b><br/>
53983  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
53984  * element will correct this<br/>
53985  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
53986  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
53987  * are unpredictable.<br/>
53988  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
53989  * grid to calculate dimensions/offsets.<br/>
53990   * @constructor
53991  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
53992  * The container MUST have some type of size defined for the grid to fill. The container will be
53993  * automatically set to position relative if it isn't already.
53994  * @param {Object} config A config object that sets properties on this grid.
53995  */
53996 Roo.grid.Grid = function(container, config){
53997         // initialize the container
53998         this.container = Roo.get(container);
53999         this.container.update("");
54000         this.container.setStyle("overflow", "hidden");
54001     this.container.addClass('x-grid-container');
54002
54003     this.id = this.container.id;
54004
54005     Roo.apply(this, config);
54006     // check and correct shorthanded configs
54007     if(this.ds){
54008         this.dataSource = this.ds;
54009         delete this.ds;
54010     }
54011     if(this.cm){
54012         this.colModel = this.cm;
54013         delete this.cm;
54014     }
54015     if(this.sm){
54016         this.selModel = this.sm;
54017         delete this.sm;
54018     }
54019
54020     if (this.selModel) {
54021         this.selModel = Roo.factory(this.selModel, Roo.grid);
54022         this.sm = this.selModel;
54023         this.sm.xmodule = this.xmodule || false;
54024     }
54025     if (typeof(this.colModel.config) == 'undefined') {
54026         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54027         this.cm = this.colModel;
54028         this.cm.xmodule = this.xmodule || false;
54029     }
54030     if (this.dataSource) {
54031         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54032         this.ds = this.dataSource;
54033         this.ds.xmodule = this.xmodule || false;
54034          
54035     }
54036     
54037     
54038     
54039     if(this.width){
54040         this.container.setWidth(this.width);
54041     }
54042
54043     if(this.height){
54044         this.container.setHeight(this.height);
54045     }
54046     /** @private */
54047         this.addEvents({
54048         // raw events
54049         /**
54050          * @event click
54051          * The raw click event for the entire grid.
54052          * @param {Roo.EventObject} e
54053          */
54054         "click" : true,
54055         /**
54056          * @event dblclick
54057          * The raw dblclick event for the entire grid.
54058          * @param {Roo.EventObject} e
54059          */
54060         "dblclick" : true,
54061         /**
54062          * @event contextmenu
54063          * The raw contextmenu event for the entire grid.
54064          * @param {Roo.EventObject} e
54065          */
54066         "contextmenu" : true,
54067         /**
54068          * @event mousedown
54069          * The raw mousedown event for the entire grid.
54070          * @param {Roo.EventObject} e
54071          */
54072         "mousedown" : true,
54073         /**
54074          * @event mouseup
54075          * The raw mouseup event for the entire grid.
54076          * @param {Roo.EventObject} e
54077          */
54078         "mouseup" : true,
54079         /**
54080          * @event mouseover
54081          * The raw mouseover event for the entire grid.
54082          * @param {Roo.EventObject} e
54083          */
54084         "mouseover" : true,
54085         /**
54086          * @event mouseout
54087          * The raw mouseout event for the entire grid.
54088          * @param {Roo.EventObject} e
54089          */
54090         "mouseout" : true,
54091         /**
54092          * @event keypress
54093          * The raw keypress event for the entire grid.
54094          * @param {Roo.EventObject} e
54095          */
54096         "keypress" : true,
54097         /**
54098          * @event keydown
54099          * The raw keydown event for the entire grid.
54100          * @param {Roo.EventObject} e
54101          */
54102         "keydown" : true,
54103
54104         // custom events
54105
54106         /**
54107          * @event cellclick
54108          * Fires when a cell is clicked
54109          * @param {Grid} this
54110          * @param {Number} rowIndex
54111          * @param {Number} columnIndex
54112          * @param {Roo.EventObject} e
54113          */
54114         "cellclick" : true,
54115         /**
54116          * @event celldblclick
54117          * Fires when a cell is double clicked
54118          * @param {Grid} this
54119          * @param {Number} rowIndex
54120          * @param {Number} columnIndex
54121          * @param {Roo.EventObject} e
54122          */
54123         "celldblclick" : true,
54124         /**
54125          * @event rowclick
54126          * Fires when a row is clicked
54127          * @param {Grid} this
54128          * @param {Number} rowIndex
54129          * @param {Roo.EventObject} e
54130          */
54131         "rowclick" : true,
54132         /**
54133          * @event rowdblclick
54134          * Fires when a row is double clicked
54135          * @param {Grid} this
54136          * @param {Number} rowIndex
54137          * @param {Roo.EventObject} e
54138          */
54139         "rowdblclick" : true,
54140         /**
54141          * @event headerclick
54142          * Fires when a header is clicked
54143          * @param {Grid} this
54144          * @param {Number} columnIndex
54145          * @param {Roo.EventObject} e
54146          */
54147         "headerclick" : true,
54148         /**
54149          * @event headerdblclick
54150          * Fires when a header cell is double clicked
54151          * @param {Grid} this
54152          * @param {Number} columnIndex
54153          * @param {Roo.EventObject} e
54154          */
54155         "headerdblclick" : true,
54156         /**
54157          * @event rowcontextmenu
54158          * Fires when a row is right clicked
54159          * @param {Grid} this
54160          * @param {Number} rowIndex
54161          * @param {Roo.EventObject} e
54162          */
54163         "rowcontextmenu" : true,
54164         /**
54165          * @event cellcontextmenu
54166          * Fires when a cell is right clicked
54167          * @param {Grid} this
54168          * @param {Number} rowIndex
54169          * @param {Number} cellIndex
54170          * @param {Roo.EventObject} e
54171          */
54172          "cellcontextmenu" : true,
54173         /**
54174          * @event headercontextmenu
54175          * Fires when a header is right clicked
54176          * @param {Grid} this
54177          * @param {Number} columnIndex
54178          * @param {Roo.EventObject} e
54179          */
54180         "headercontextmenu" : true,
54181         /**
54182          * @event bodyscroll
54183          * Fires when the body element is scrolled
54184          * @param {Number} scrollLeft
54185          * @param {Number} scrollTop
54186          */
54187         "bodyscroll" : true,
54188         /**
54189          * @event columnresize
54190          * Fires when the user resizes a column
54191          * @param {Number} columnIndex
54192          * @param {Number} newSize
54193          */
54194         "columnresize" : true,
54195         /**
54196          * @event columnmove
54197          * Fires when the user moves a column
54198          * @param {Number} oldIndex
54199          * @param {Number} newIndex
54200          */
54201         "columnmove" : true,
54202         /**
54203          * @event startdrag
54204          * Fires when row(s) start being dragged
54205          * @param {Grid} this
54206          * @param {Roo.GridDD} dd The drag drop object
54207          * @param {event} e The raw browser event
54208          */
54209         "startdrag" : true,
54210         /**
54211          * @event enddrag
54212          * Fires when a drag operation is complete
54213          * @param {Grid} this
54214          * @param {Roo.GridDD} dd The drag drop object
54215          * @param {event} e The raw browser event
54216          */
54217         "enddrag" : true,
54218         /**
54219          * @event dragdrop
54220          * Fires when dragged row(s) are dropped on a valid DD target
54221          * @param {Grid} this
54222          * @param {Roo.GridDD} dd The drag drop object
54223          * @param {String} targetId The target drag drop object
54224          * @param {event} e The raw browser event
54225          */
54226         "dragdrop" : true,
54227         /**
54228          * @event dragover
54229          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54230          * @param {Grid} this
54231          * @param {Roo.GridDD} dd The drag drop object
54232          * @param {String} targetId The target drag drop object
54233          * @param {event} e The raw browser event
54234          */
54235         "dragover" : true,
54236         /**
54237          * @event dragenter
54238          *  Fires when the dragged row(s) first cross another DD target while being dragged
54239          * @param {Grid} this
54240          * @param {Roo.GridDD} dd The drag drop object
54241          * @param {String} targetId The target drag drop object
54242          * @param {event} e The raw browser event
54243          */
54244         "dragenter" : true,
54245         /**
54246          * @event dragout
54247          * Fires when the dragged row(s) leave another DD target while being dragged
54248          * @param {Grid} this
54249          * @param {Roo.GridDD} dd The drag drop object
54250          * @param {String} targetId The target drag drop object
54251          * @param {event} e The raw browser event
54252          */
54253         "dragout" : true,
54254         /**
54255          * @event rowclass
54256          * Fires when a row is rendered, so you can change add a style to it.
54257          * @param {GridView} gridview   The grid view
54258          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54259          */
54260         'rowclass' : true,
54261
54262         /**
54263          * @event render
54264          * Fires when the grid is rendered
54265          * @param {Grid} grid
54266          */
54267         'render' : true
54268     });
54269
54270     Roo.grid.Grid.superclass.constructor.call(this);
54271 };
54272 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54273     
54274     /**
54275      * @cfg {String} ddGroup - drag drop group.
54276      */
54277
54278     /**
54279      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54280      */
54281     minColumnWidth : 25,
54282
54283     /**
54284      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54285      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54286      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54287      */
54288     autoSizeColumns : false,
54289
54290     /**
54291      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54292      */
54293     autoSizeHeaders : true,
54294
54295     /**
54296      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54297      */
54298     monitorWindowResize : true,
54299
54300     /**
54301      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54302      * rows measured to get a columns size. Default is 0 (all rows).
54303      */
54304     maxRowsToMeasure : 0,
54305
54306     /**
54307      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54308      */
54309     trackMouseOver : true,
54310
54311     /**
54312     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54313     */
54314     
54315     /**
54316     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54317     */
54318     enableDragDrop : false,
54319     
54320     /**
54321     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54322     */
54323     enableColumnMove : true,
54324     
54325     /**
54326     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54327     */
54328     enableColumnHide : true,
54329     
54330     /**
54331     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54332     */
54333     enableRowHeightSync : false,
54334     
54335     /**
54336     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54337     */
54338     stripeRows : true,
54339     
54340     /**
54341     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54342     */
54343     autoHeight : false,
54344
54345     /**
54346      * @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.
54347      */
54348     autoExpandColumn : false,
54349
54350     /**
54351     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54352     * Default is 50.
54353     */
54354     autoExpandMin : 50,
54355
54356     /**
54357     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54358     */
54359     autoExpandMax : 1000,
54360
54361     /**
54362     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54363     */
54364     view : null,
54365
54366     /**
54367     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54368     */
54369     loadMask : false,
54370     /**
54371     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54372     */
54373     dropTarget: false,
54374     
54375    
54376     
54377     // private
54378     rendered : false,
54379
54380     /**
54381     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54382     * of a fixed width. Default is false.
54383     */
54384     /**
54385     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54386     */
54387     /**
54388      * Called once after all setup has been completed and the grid is ready to be rendered.
54389      * @return {Roo.grid.Grid} this
54390      */
54391     render : function()
54392     {
54393         var c = this.container;
54394         // try to detect autoHeight/width mode
54395         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54396             this.autoHeight = true;
54397         }
54398         var view = this.getView();
54399         view.init(this);
54400
54401         c.on("click", this.onClick, this);
54402         c.on("dblclick", this.onDblClick, this);
54403         c.on("contextmenu", this.onContextMenu, this);
54404         c.on("keydown", this.onKeyDown, this);
54405         if (Roo.isTouch) {
54406             c.on("touchstart", this.onTouchStart, this);
54407         }
54408
54409         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54410
54411         this.getSelectionModel().init(this);
54412
54413         view.render();
54414
54415         if(this.loadMask){
54416             this.loadMask = new Roo.LoadMask(this.container,
54417                     Roo.apply({store:this.dataSource}, this.loadMask));
54418         }
54419         
54420         
54421         if (this.toolbar && this.toolbar.xtype) {
54422             this.toolbar.container = this.getView().getHeaderPanel(true);
54423             this.toolbar = new Roo.Toolbar(this.toolbar);
54424         }
54425         if (this.footer && this.footer.xtype) {
54426             this.footer.dataSource = this.getDataSource();
54427             this.footer.container = this.getView().getFooterPanel(true);
54428             this.footer = Roo.factory(this.footer, Roo);
54429         }
54430         if (this.dropTarget && this.dropTarget.xtype) {
54431             delete this.dropTarget.xtype;
54432             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54433         }
54434         
54435         
54436         this.rendered = true;
54437         this.fireEvent('render', this);
54438         return this;
54439     },
54440
54441         /**
54442          * Reconfigures the grid to use a different Store and Column Model.
54443          * The View will be bound to the new objects and refreshed.
54444          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54445          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54446          */
54447     reconfigure : function(dataSource, colModel){
54448         if(this.loadMask){
54449             this.loadMask.destroy();
54450             this.loadMask = new Roo.LoadMask(this.container,
54451                     Roo.apply({store:dataSource}, this.loadMask));
54452         }
54453         this.view.bind(dataSource, colModel);
54454         this.dataSource = dataSource;
54455         this.colModel = colModel;
54456         this.view.refresh(true);
54457     },
54458
54459     // private
54460     onKeyDown : function(e){
54461         this.fireEvent("keydown", e);
54462     },
54463
54464     /**
54465      * Destroy this grid.
54466      * @param {Boolean} removeEl True to remove the element
54467      */
54468     destroy : function(removeEl, keepListeners){
54469         if(this.loadMask){
54470             this.loadMask.destroy();
54471         }
54472         var c = this.container;
54473         c.removeAllListeners();
54474         this.view.destroy();
54475         this.colModel.purgeListeners();
54476         if(!keepListeners){
54477             this.purgeListeners();
54478         }
54479         c.update("");
54480         if(removeEl === true){
54481             c.remove();
54482         }
54483     },
54484
54485     // private
54486     processEvent : function(name, e){
54487         // does this fire select???
54488         //Roo.log('grid:processEvent '  + name);
54489         
54490         if (name != 'touchstart' ) {
54491             this.fireEvent(name, e);    
54492         }
54493         
54494         var t = e.getTarget();
54495         var v = this.view;
54496         var header = v.findHeaderIndex(t);
54497         if(header !== false){
54498             var ename = name == 'touchstart' ? 'click' : name;
54499              
54500             this.fireEvent("header" + ename, this, header, e);
54501         }else{
54502             var row = v.findRowIndex(t);
54503             var cell = v.findCellIndex(t);
54504             if (name == 'touchstart') {
54505                 // first touch is always a click.
54506                 // hopefull this happens after selection is updated.?
54507                 name = false;
54508                 
54509                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54510                     var cs = this.selModel.getSelectedCell();
54511                     if (row == cs[0] && cell == cs[1]){
54512                         name = 'dblclick';
54513                     }
54514                 }
54515                 if (typeof(this.selModel.getSelections) != 'undefined') {
54516                     var cs = this.selModel.getSelections();
54517                     var ds = this.dataSource;
54518                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54519                         name = 'dblclick';
54520                     }
54521                 }
54522                 if (!name) {
54523                     return;
54524                 }
54525             }
54526             
54527             
54528             if(row !== false){
54529                 this.fireEvent("row" + name, this, row, e);
54530                 if(cell !== false){
54531                     this.fireEvent("cell" + name, this, row, cell, e);
54532                 }
54533             }
54534         }
54535     },
54536
54537     // private
54538     onClick : function(e){
54539         this.processEvent("click", e);
54540     },
54541    // private
54542     onTouchStart : function(e){
54543         this.processEvent("touchstart", e);
54544     },
54545
54546     // private
54547     onContextMenu : function(e, t){
54548         this.processEvent("contextmenu", e);
54549     },
54550
54551     // private
54552     onDblClick : function(e){
54553         this.processEvent("dblclick", e);
54554     },
54555
54556     // private
54557     walkCells : function(row, col, step, fn, scope){
54558         var cm = this.colModel, clen = cm.getColumnCount();
54559         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54560         if(step < 0){
54561             if(col < 0){
54562                 row--;
54563                 first = false;
54564             }
54565             while(row >= 0){
54566                 if(!first){
54567                     col = clen-1;
54568                 }
54569                 first = false;
54570                 while(col >= 0){
54571                     if(fn.call(scope || this, row, col, cm) === true){
54572                         return [row, col];
54573                     }
54574                     col--;
54575                 }
54576                 row--;
54577             }
54578         } else {
54579             if(col >= clen){
54580                 row++;
54581                 first = false;
54582             }
54583             while(row < rlen){
54584                 if(!first){
54585                     col = 0;
54586                 }
54587                 first = false;
54588                 while(col < clen){
54589                     if(fn.call(scope || this, row, col, cm) === true){
54590                         return [row, col];
54591                     }
54592                     col++;
54593                 }
54594                 row++;
54595             }
54596         }
54597         return null;
54598     },
54599
54600     // private
54601     getSelections : function(){
54602         return this.selModel.getSelections();
54603     },
54604
54605     /**
54606      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54607      * but if manual update is required this method will initiate it.
54608      */
54609     autoSize : function(){
54610         if(this.rendered){
54611             this.view.layout();
54612             if(this.view.adjustForScroll){
54613                 this.view.adjustForScroll();
54614             }
54615         }
54616     },
54617
54618     /**
54619      * Returns the grid's underlying element.
54620      * @return {Element} The element
54621      */
54622     getGridEl : function(){
54623         return this.container;
54624     },
54625
54626     // private for compatibility, overridden by editor grid
54627     stopEditing : function(){},
54628
54629     /**
54630      * Returns the grid's SelectionModel.
54631      * @return {SelectionModel}
54632      */
54633     getSelectionModel : function(){
54634         if(!this.selModel){
54635             this.selModel = new Roo.grid.RowSelectionModel();
54636         }
54637         return this.selModel;
54638     },
54639
54640     /**
54641      * Returns the grid's DataSource.
54642      * @return {DataSource}
54643      */
54644     getDataSource : function(){
54645         return this.dataSource;
54646     },
54647
54648     /**
54649      * Returns the grid's ColumnModel.
54650      * @return {ColumnModel}
54651      */
54652     getColumnModel : function(){
54653         return this.colModel;
54654     },
54655
54656     /**
54657      * Returns the grid's GridView object.
54658      * @return {GridView}
54659      */
54660     getView : function(){
54661         if(!this.view){
54662             this.view = new Roo.grid.GridView(this.viewConfig);
54663         }
54664         return this.view;
54665     },
54666     /**
54667      * Called to get grid's drag proxy text, by default returns this.ddText.
54668      * @return {String}
54669      */
54670     getDragDropText : function(){
54671         var count = this.selModel.getCount();
54672         return String.format(this.ddText, count, count == 1 ? '' : 's');
54673     }
54674 });
54675 /**
54676  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54677  * %0 is replaced with the number of selected rows.
54678  * @type String
54679  */
54680 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54681  * Based on:
54682  * Ext JS Library 1.1.1
54683  * Copyright(c) 2006-2007, Ext JS, LLC.
54684  *
54685  * Originally Released Under LGPL - original licence link has changed is not relivant.
54686  *
54687  * Fork - LGPL
54688  * <script type="text/javascript">
54689  */
54690  
54691 Roo.grid.AbstractGridView = function(){
54692         this.grid = null;
54693         
54694         this.events = {
54695             "beforerowremoved" : true,
54696             "beforerowsinserted" : true,
54697             "beforerefresh" : true,
54698             "rowremoved" : true,
54699             "rowsinserted" : true,
54700             "rowupdated" : true,
54701             "refresh" : true
54702         };
54703     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54704 };
54705
54706 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54707     rowClass : "x-grid-row",
54708     cellClass : "x-grid-cell",
54709     tdClass : "x-grid-td",
54710     hdClass : "x-grid-hd",
54711     splitClass : "x-grid-hd-split",
54712     
54713     init: function(grid){
54714         this.grid = grid;
54715                 var cid = this.grid.getGridEl().id;
54716         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54717         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54718         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54719         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54720         },
54721         
54722     getColumnRenderers : function(){
54723         var renderers = [];
54724         var cm = this.grid.colModel;
54725         var colCount = cm.getColumnCount();
54726         for(var i = 0; i < colCount; i++){
54727             renderers[i] = cm.getRenderer(i);
54728         }
54729         return renderers;
54730     },
54731     
54732     getColumnIds : function(){
54733         var ids = [];
54734         var cm = this.grid.colModel;
54735         var colCount = cm.getColumnCount();
54736         for(var i = 0; i < colCount; i++){
54737             ids[i] = cm.getColumnId(i);
54738         }
54739         return ids;
54740     },
54741     
54742     getDataIndexes : function(){
54743         if(!this.indexMap){
54744             this.indexMap = this.buildIndexMap();
54745         }
54746         return this.indexMap.colToData;
54747     },
54748     
54749     getColumnIndexByDataIndex : function(dataIndex){
54750         if(!this.indexMap){
54751             this.indexMap = this.buildIndexMap();
54752         }
54753         return this.indexMap.dataToCol[dataIndex];
54754     },
54755     
54756     /**
54757      * Set a css style for a column dynamically. 
54758      * @param {Number} colIndex The index of the column
54759      * @param {String} name The css property name
54760      * @param {String} value The css value
54761      */
54762     setCSSStyle : function(colIndex, name, value){
54763         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54764         Roo.util.CSS.updateRule(selector, name, value);
54765     },
54766     
54767     generateRules : function(cm){
54768         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54769         Roo.util.CSS.removeStyleSheet(rulesId);
54770         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54771             var cid = cm.getColumnId(i);
54772             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54773                          this.tdSelector, cid, " {\n}\n",
54774                          this.hdSelector, cid, " {\n}\n",
54775                          this.splitSelector, cid, " {\n}\n");
54776         }
54777         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54778     }
54779 });/*
54780  * Based on:
54781  * Ext JS Library 1.1.1
54782  * Copyright(c) 2006-2007, Ext JS, LLC.
54783  *
54784  * Originally Released Under LGPL - original licence link has changed is not relivant.
54785  *
54786  * Fork - LGPL
54787  * <script type="text/javascript">
54788  */
54789
54790 // private
54791 // This is a support class used internally by the Grid components
54792 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54793     this.grid = grid;
54794     this.view = grid.getView();
54795     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54796     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54797     if(hd2){
54798         this.setHandleElId(Roo.id(hd));
54799         this.setOuterHandleElId(Roo.id(hd2));
54800     }
54801     this.scroll = false;
54802 };
54803 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54804     maxDragWidth: 120,
54805     getDragData : function(e){
54806         var t = Roo.lib.Event.getTarget(e);
54807         var h = this.view.findHeaderCell(t);
54808         if(h){
54809             return {ddel: h.firstChild, header:h};
54810         }
54811         return false;
54812     },
54813
54814     onInitDrag : function(e){
54815         this.view.headersDisabled = true;
54816         var clone = this.dragData.ddel.cloneNode(true);
54817         clone.id = Roo.id();
54818         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54819         this.proxy.update(clone);
54820         return true;
54821     },
54822
54823     afterValidDrop : function(){
54824         var v = this.view;
54825         setTimeout(function(){
54826             v.headersDisabled = false;
54827         }, 50);
54828     },
54829
54830     afterInvalidDrop : function(){
54831         var v = this.view;
54832         setTimeout(function(){
54833             v.headersDisabled = false;
54834         }, 50);
54835     }
54836 });
54837 /*
54838  * Based on:
54839  * Ext JS Library 1.1.1
54840  * Copyright(c) 2006-2007, Ext JS, LLC.
54841  *
54842  * Originally Released Under LGPL - original licence link has changed is not relivant.
54843  *
54844  * Fork - LGPL
54845  * <script type="text/javascript">
54846  */
54847 // private
54848 // This is a support class used internally by the Grid components
54849 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54850     this.grid = grid;
54851     this.view = grid.getView();
54852     // split the proxies so they don't interfere with mouse events
54853     this.proxyTop = Roo.DomHelper.append(document.body, {
54854         cls:"col-move-top", html:"&#160;"
54855     }, true);
54856     this.proxyBottom = Roo.DomHelper.append(document.body, {
54857         cls:"col-move-bottom", html:"&#160;"
54858     }, true);
54859     this.proxyTop.hide = this.proxyBottom.hide = function(){
54860         this.setLeftTop(-100,-100);
54861         this.setStyle("visibility", "hidden");
54862     };
54863     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54864     // temporarily disabled
54865     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54866     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54867 };
54868 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54869     proxyOffsets : [-4, -9],
54870     fly: Roo.Element.fly,
54871
54872     getTargetFromEvent : function(e){
54873         var t = Roo.lib.Event.getTarget(e);
54874         var cindex = this.view.findCellIndex(t);
54875         if(cindex !== false){
54876             return this.view.getHeaderCell(cindex);
54877         }
54878         return null;
54879     },
54880
54881     nextVisible : function(h){
54882         var v = this.view, cm = this.grid.colModel;
54883         h = h.nextSibling;
54884         while(h){
54885             if(!cm.isHidden(v.getCellIndex(h))){
54886                 return h;
54887             }
54888             h = h.nextSibling;
54889         }
54890         return null;
54891     },
54892
54893     prevVisible : function(h){
54894         var v = this.view, cm = this.grid.colModel;
54895         h = h.prevSibling;
54896         while(h){
54897             if(!cm.isHidden(v.getCellIndex(h))){
54898                 return h;
54899             }
54900             h = h.prevSibling;
54901         }
54902         return null;
54903     },
54904
54905     positionIndicator : function(h, n, e){
54906         var x = Roo.lib.Event.getPageX(e);
54907         var r = Roo.lib.Dom.getRegion(n.firstChild);
54908         var px, pt, py = r.top + this.proxyOffsets[1];
54909         if((r.right - x) <= (r.right-r.left)/2){
54910             px = r.right+this.view.borderWidth;
54911             pt = "after";
54912         }else{
54913             px = r.left;
54914             pt = "before";
54915         }
54916         var oldIndex = this.view.getCellIndex(h);
54917         var newIndex = this.view.getCellIndex(n);
54918
54919         if(this.grid.colModel.isFixed(newIndex)){
54920             return false;
54921         }
54922
54923         var locked = this.grid.colModel.isLocked(newIndex);
54924
54925         if(pt == "after"){
54926             newIndex++;
54927         }
54928         if(oldIndex < newIndex){
54929             newIndex--;
54930         }
54931         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54932             return false;
54933         }
54934         px +=  this.proxyOffsets[0];
54935         this.proxyTop.setLeftTop(px, py);
54936         this.proxyTop.show();
54937         if(!this.bottomOffset){
54938             this.bottomOffset = this.view.mainHd.getHeight();
54939         }
54940         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54941         this.proxyBottom.show();
54942         return pt;
54943     },
54944
54945     onNodeEnter : function(n, dd, e, data){
54946         if(data.header != n){
54947             this.positionIndicator(data.header, n, e);
54948         }
54949     },
54950
54951     onNodeOver : function(n, dd, e, data){
54952         var result = false;
54953         if(data.header != n){
54954             result = this.positionIndicator(data.header, n, e);
54955         }
54956         if(!result){
54957             this.proxyTop.hide();
54958             this.proxyBottom.hide();
54959         }
54960         return result ? this.dropAllowed : this.dropNotAllowed;
54961     },
54962
54963     onNodeOut : function(n, dd, e, data){
54964         this.proxyTop.hide();
54965         this.proxyBottom.hide();
54966     },
54967
54968     onNodeDrop : function(n, dd, e, data){
54969         var h = data.header;
54970         if(h != n){
54971             var cm = this.grid.colModel;
54972             var x = Roo.lib.Event.getPageX(e);
54973             var r = Roo.lib.Dom.getRegion(n.firstChild);
54974             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
54975             var oldIndex = this.view.getCellIndex(h);
54976             var newIndex = this.view.getCellIndex(n);
54977             var locked = cm.isLocked(newIndex);
54978             if(pt == "after"){
54979                 newIndex++;
54980             }
54981             if(oldIndex < newIndex){
54982                 newIndex--;
54983             }
54984             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
54985                 return false;
54986             }
54987             cm.setLocked(oldIndex, locked, true);
54988             cm.moveColumn(oldIndex, newIndex);
54989             this.grid.fireEvent("columnmove", oldIndex, newIndex);
54990             return true;
54991         }
54992         return false;
54993     }
54994 });
54995 /*
54996  * Based on:
54997  * Ext JS Library 1.1.1
54998  * Copyright(c) 2006-2007, Ext JS, LLC.
54999  *
55000  * Originally Released Under LGPL - original licence link has changed is not relivant.
55001  *
55002  * Fork - LGPL
55003  * <script type="text/javascript">
55004  */
55005   
55006 /**
55007  * @class Roo.grid.GridView
55008  * @extends Roo.util.Observable
55009  *
55010  * @constructor
55011  * @param {Object} config
55012  */
55013 Roo.grid.GridView = function(config){
55014     Roo.grid.GridView.superclass.constructor.call(this);
55015     this.el = null;
55016
55017     Roo.apply(this, config);
55018 };
55019
55020 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55021
55022     unselectable :  'unselectable="on"',
55023     unselectableCls :  'x-unselectable',
55024     
55025     
55026     rowClass : "x-grid-row",
55027
55028     cellClass : "x-grid-col",
55029
55030     tdClass : "x-grid-td",
55031
55032     hdClass : "x-grid-hd",
55033
55034     splitClass : "x-grid-split",
55035
55036     sortClasses : ["sort-asc", "sort-desc"],
55037
55038     enableMoveAnim : false,
55039
55040     hlColor: "C3DAF9",
55041
55042     dh : Roo.DomHelper,
55043
55044     fly : Roo.Element.fly,
55045
55046     css : Roo.util.CSS,
55047
55048     borderWidth: 1,
55049
55050     splitOffset: 3,
55051
55052     scrollIncrement : 22,
55053
55054     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55055
55056     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55057
55058     bind : function(ds, cm){
55059         if(this.ds){
55060             this.ds.un("load", this.onLoad, this);
55061             this.ds.un("datachanged", this.onDataChange, this);
55062             this.ds.un("add", this.onAdd, this);
55063             this.ds.un("remove", this.onRemove, this);
55064             this.ds.un("update", this.onUpdate, this);
55065             this.ds.un("clear", this.onClear, this);
55066         }
55067         if(ds){
55068             ds.on("load", this.onLoad, this);
55069             ds.on("datachanged", this.onDataChange, this);
55070             ds.on("add", this.onAdd, this);
55071             ds.on("remove", this.onRemove, this);
55072             ds.on("update", this.onUpdate, this);
55073             ds.on("clear", this.onClear, this);
55074         }
55075         this.ds = ds;
55076
55077         if(this.cm){
55078             this.cm.un("widthchange", this.onColWidthChange, this);
55079             this.cm.un("headerchange", this.onHeaderChange, this);
55080             this.cm.un("hiddenchange", this.onHiddenChange, this);
55081             this.cm.un("columnmoved", this.onColumnMove, this);
55082             this.cm.un("columnlockchange", this.onColumnLock, this);
55083         }
55084         if(cm){
55085             this.generateRules(cm);
55086             cm.on("widthchange", this.onColWidthChange, this);
55087             cm.on("headerchange", this.onHeaderChange, this);
55088             cm.on("hiddenchange", this.onHiddenChange, this);
55089             cm.on("columnmoved", this.onColumnMove, this);
55090             cm.on("columnlockchange", this.onColumnLock, this);
55091         }
55092         this.cm = cm;
55093     },
55094
55095     init: function(grid){
55096         Roo.grid.GridView.superclass.init.call(this, grid);
55097
55098         this.bind(grid.dataSource, grid.colModel);
55099
55100         grid.on("headerclick", this.handleHeaderClick, this);
55101
55102         if(grid.trackMouseOver){
55103             grid.on("mouseover", this.onRowOver, this);
55104             grid.on("mouseout", this.onRowOut, this);
55105         }
55106         grid.cancelTextSelection = function(){};
55107         this.gridId = grid.id;
55108
55109         var tpls = this.templates || {};
55110
55111         if(!tpls.master){
55112             tpls.master = new Roo.Template(
55113                '<div class="x-grid" hidefocus="true">',
55114                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55115                   '<div class="x-grid-topbar"></div>',
55116                   '<div class="x-grid-scroller"><div></div></div>',
55117                   '<div class="x-grid-locked">',
55118                       '<div class="x-grid-header">{lockedHeader}</div>',
55119                       '<div class="x-grid-body">{lockedBody}</div>',
55120                   "</div>",
55121                   '<div class="x-grid-viewport">',
55122                       '<div class="x-grid-header">{header}</div>',
55123                       '<div class="x-grid-body">{body}</div>',
55124                   "</div>",
55125                   '<div class="x-grid-bottombar"></div>',
55126                  
55127                   '<div class="x-grid-resize-proxy">&#160;</div>',
55128                "</div>"
55129             );
55130             tpls.master.disableformats = true;
55131         }
55132
55133         if(!tpls.header){
55134             tpls.header = new Roo.Template(
55135                '<table border="0" cellspacing="0" cellpadding="0">',
55136                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55137                "</table>{splits}"
55138             );
55139             tpls.header.disableformats = true;
55140         }
55141         tpls.header.compile();
55142
55143         if(!tpls.hcell){
55144             tpls.hcell = new Roo.Template(
55145                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55146                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55147                 "</div></td>"
55148              );
55149              tpls.hcell.disableFormats = true;
55150         }
55151         tpls.hcell.compile();
55152
55153         if(!tpls.hsplit){
55154             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55155                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55156             tpls.hsplit.disableFormats = true;
55157         }
55158         tpls.hsplit.compile();
55159
55160         if(!tpls.body){
55161             tpls.body = new Roo.Template(
55162                '<table border="0" cellspacing="0" cellpadding="0">',
55163                "<tbody>{rows}</tbody>",
55164                "</table>"
55165             );
55166             tpls.body.disableFormats = true;
55167         }
55168         tpls.body.compile();
55169
55170         if(!tpls.row){
55171             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55172             tpls.row.disableFormats = true;
55173         }
55174         tpls.row.compile();
55175
55176         if(!tpls.cell){
55177             tpls.cell = new Roo.Template(
55178                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55179                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55180                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55181                 "</td>"
55182             );
55183             tpls.cell.disableFormats = true;
55184         }
55185         tpls.cell.compile();
55186
55187         this.templates = tpls;
55188     },
55189
55190     // remap these for backwards compat
55191     onColWidthChange : function(){
55192         this.updateColumns.apply(this, arguments);
55193     },
55194     onHeaderChange : function(){
55195         this.updateHeaders.apply(this, arguments);
55196     }, 
55197     onHiddenChange : function(){
55198         this.handleHiddenChange.apply(this, arguments);
55199     },
55200     onColumnMove : function(){
55201         this.handleColumnMove.apply(this, arguments);
55202     },
55203     onColumnLock : function(){
55204         this.handleLockChange.apply(this, arguments);
55205     },
55206
55207     onDataChange : function(){
55208         this.refresh();
55209         this.updateHeaderSortState();
55210     },
55211
55212     onClear : function(){
55213         this.refresh();
55214     },
55215
55216     onUpdate : function(ds, record){
55217         this.refreshRow(record);
55218     },
55219
55220     refreshRow : function(record){
55221         var ds = this.ds, index;
55222         if(typeof record == 'number'){
55223             index = record;
55224             record = ds.getAt(index);
55225         }else{
55226             index = ds.indexOf(record);
55227         }
55228         this.insertRows(ds, index, index, true);
55229         this.onRemove(ds, record, index+1, true);
55230         this.syncRowHeights(index, index);
55231         this.layout();
55232         this.fireEvent("rowupdated", this, index, record);
55233     },
55234
55235     onAdd : function(ds, records, index){
55236         this.insertRows(ds, index, index + (records.length-1));
55237     },
55238
55239     onRemove : function(ds, record, index, isUpdate){
55240         if(isUpdate !== true){
55241             this.fireEvent("beforerowremoved", this, index, record);
55242         }
55243         var bt = this.getBodyTable(), lt = this.getLockedTable();
55244         if(bt.rows[index]){
55245             bt.firstChild.removeChild(bt.rows[index]);
55246         }
55247         if(lt.rows[index]){
55248             lt.firstChild.removeChild(lt.rows[index]);
55249         }
55250         if(isUpdate !== true){
55251             this.stripeRows(index);
55252             this.syncRowHeights(index, index);
55253             this.layout();
55254             this.fireEvent("rowremoved", this, index, record);
55255         }
55256     },
55257
55258     onLoad : function(){
55259         this.scrollToTop();
55260     },
55261
55262     /**
55263      * Scrolls the grid to the top
55264      */
55265     scrollToTop : function(){
55266         if(this.scroller){
55267             this.scroller.dom.scrollTop = 0;
55268             this.syncScroll();
55269         }
55270     },
55271
55272     /**
55273      * Gets a panel in the header of the grid that can be used for toolbars etc.
55274      * After modifying the contents of this panel a call to grid.autoSize() may be
55275      * required to register any changes in size.
55276      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55277      * @return Roo.Element
55278      */
55279     getHeaderPanel : function(doShow){
55280         if(doShow){
55281             this.headerPanel.show();
55282         }
55283         return this.headerPanel;
55284     },
55285
55286     /**
55287      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55288      * After modifying the contents of this panel a call to grid.autoSize() may be
55289      * required to register any changes in size.
55290      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55291      * @return Roo.Element
55292      */
55293     getFooterPanel : function(doShow){
55294         if(doShow){
55295             this.footerPanel.show();
55296         }
55297         return this.footerPanel;
55298     },
55299
55300     initElements : function(){
55301         var E = Roo.Element;
55302         var el = this.grid.getGridEl().dom.firstChild;
55303         var cs = el.childNodes;
55304
55305         this.el = new E(el);
55306         
55307          this.focusEl = new E(el.firstChild);
55308         this.focusEl.swallowEvent("click", true);
55309         
55310         this.headerPanel = new E(cs[1]);
55311         this.headerPanel.enableDisplayMode("block");
55312
55313         this.scroller = new E(cs[2]);
55314         this.scrollSizer = new E(this.scroller.dom.firstChild);
55315
55316         this.lockedWrap = new E(cs[3]);
55317         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55318         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55319
55320         this.mainWrap = new E(cs[4]);
55321         this.mainHd = new E(this.mainWrap.dom.firstChild);
55322         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55323
55324         this.footerPanel = new E(cs[5]);
55325         this.footerPanel.enableDisplayMode("block");
55326
55327         this.resizeProxy = new E(cs[6]);
55328
55329         this.headerSelector = String.format(
55330            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55331            this.lockedHd.id, this.mainHd.id
55332         );
55333
55334         this.splitterSelector = String.format(
55335            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55336            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55337         );
55338     },
55339     idToCssName : function(s)
55340     {
55341         return s.replace(/[^a-z0-9]+/ig, '-');
55342     },
55343
55344     getHeaderCell : function(index){
55345         return Roo.DomQuery.select(this.headerSelector)[index];
55346     },
55347
55348     getHeaderCellMeasure : function(index){
55349         return this.getHeaderCell(index).firstChild;
55350     },
55351
55352     getHeaderCellText : function(index){
55353         return this.getHeaderCell(index).firstChild.firstChild;
55354     },
55355
55356     getLockedTable : function(){
55357         return this.lockedBody.dom.firstChild;
55358     },
55359
55360     getBodyTable : function(){
55361         return this.mainBody.dom.firstChild;
55362     },
55363
55364     getLockedRow : function(index){
55365         return this.getLockedTable().rows[index];
55366     },
55367
55368     getRow : function(index){
55369         return this.getBodyTable().rows[index];
55370     },
55371
55372     getRowComposite : function(index){
55373         if(!this.rowEl){
55374             this.rowEl = new Roo.CompositeElementLite();
55375         }
55376         var els = [], lrow, mrow;
55377         if(lrow = this.getLockedRow(index)){
55378             els.push(lrow);
55379         }
55380         if(mrow = this.getRow(index)){
55381             els.push(mrow);
55382         }
55383         this.rowEl.elements = els;
55384         return this.rowEl;
55385     },
55386     /**
55387      * Gets the 'td' of the cell
55388      * 
55389      * @param {Integer} rowIndex row to select
55390      * @param {Integer} colIndex column to select
55391      * 
55392      * @return {Object} 
55393      */
55394     getCell : function(rowIndex, colIndex){
55395         var locked = this.cm.getLockedCount();
55396         var source;
55397         if(colIndex < locked){
55398             source = this.lockedBody.dom.firstChild;
55399         }else{
55400             source = this.mainBody.dom.firstChild;
55401             colIndex -= locked;
55402         }
55403         return source.rows[rowIndex].childNodes[colIndex];
55404     },
55405
55406     getCellText : function(rowIndex, colIndex){
55407         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55408     },
55409
55410     getCellBox : function(cell){
55411         var b = this.fly(cell).getBox();
55412         if(Roo.isOpera){ // opera fails to report the Y
55413             b.y = cell.offsetTop + this.mainBody.getY();
55414         }
55415         return b;
55416     },
55417
55418     getCellIndex : function(cell){
55419         var id = String(cell.className).match(this.cellRE);
55420         if(id){
55421             return parseInt(id[1], 10);
55422         }
55423         return 0;
55424     },
55425
55426     findHeaderIndex : function(n){
55427         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55428         return r ? this.getCellIndex(r) : false;
55429     },
55430
55431     findHeaderCell : function(n){
55432         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55433         return r ? r : false;
55434     },
55435
55436     findRowIndex : function(n){
55437         if(!n){
55438             return false;
55439         }
55440         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55441         return r ? r.rowIndex : false;
55442     },
55443
55444     findCellIndex : function(node){
55445         var stop = this.el.dom;
55446         while(node && node != stop){
55447             if(this.findRE.test(node.className)){
55448                 return this.getCellIndex(node);
55449             }
55450             node = node.parentNode;
55451         }
55452         return false;
55453     },
55454
55455     getColumnId : function(index){
55456         return this.cm.getColumnId(index);
55457     },
55458
55459     getSplitters : function()
55460     {
55461         if(this.splitterSelector){
55462            return Roo.DomQuery.select(this.splitterSelector);
55463         }else{
55464             return null;
55465       }
55466     },
55467
55468     getSplitter : function(index){
55469         return this.getSplitters()[index];
55470     },
55471
55472     onRowOver : function(e, t){
55473         var row;
55474         if((row = this.findRowIndex(t)) !== false){
55475             this.getRowComposite(row).addClass("x-grid-row-over");
55476         }
55477     },
55478
55479     onRowOut : function(e, t){
55480         var row;
55481         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55482             this.getRowComposite(row).removeClass("x-grid-row-over");
55483         }
55484     },
55485
55486     renderHeaders : function(){
55487         var cm = this.cm;
55488         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55489         var cb = [], lb = [], sb = [], lsb = [], p = {};
55490         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55491             p.cellId = "x-grid-hd-0-" + i;
55492             p.splitId = "x-grid-csplit-0-" + i;
55493             p.id = cm.getColumnId(i);
55494             p.value = cm.getColumnHeader(i) || "";
55495             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55496             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55497             if(!cm.isLocked(i)){
55498                 cb[cb.length] = ct.apply(p);
55499                 sb[sb.length] = st.apply(p);
55500             }else{
55501                 lb[lb.length] = ct.apply(p);
55502                 lsb[lsb.length] = st.apply(p);
55503             }
55504         }
55505         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55506                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55507     },
55508
55509     updateHeaders : function(){
55510         var html = this.renderHeaders();
55511         this.lockedHd.update(html[0]);
55512         this.mainHd.update(html[1]);
55513     },
55514
55515     /**
55516      * Focuses the specified row.
55517      * @param {Number} row The row index
55518      */
55519     focusRow : function(row)
55520     {
55521         //Roo.log('GridView.focusRow');
55522         var x = this.scroller.dom.scrollLeft;
55523         this.focusCell(row, 0, false);
55524         this.scroller.dom.scrollLeft = x;
55525     },
55526
55527     /**
55528      * Focuses the specified cell.
55529      * @param {Number} row The row index
55530      * @param {Number} col The column index
55531      * @param {Boolean} hscroll false to disable horizontal scrolling
55532      */
55533     focusCell : function(row, col, hscroll)
55534     {
55535         //Roo.log('GridView.focusCell');
55536         var el = this.ensureVisible(row, col, hscroll);
55537         this.focusEl.alignTo(el, "tl-tl");
55538         if(Roo.isGecko){
55539             this.focusEl.focus();
55540         }else{
55541             this.focusEl.focus.defer(1, this.focusEl);
55542         }
55543     },
55544
55545     /**
55546      * Scrolls the specified cell into view
55547      * @param {Number} row The row index
55548      * @param {Number} col The column index
55549      * @param {Boolean} hscroll false to disable horizontal scrolling
55550      */
55551     ensureVisible : function(row, col, hscroll)
55552     {
55553         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55554         //return null; //disable for testing.
55555         if(typeof row != "number"){
55556             row = row.rowIndex;
55557         }
55558         if(row < 0 && row >= this.ds.getCount()){
55559             return  null;
55560         }
55561         col = (col !== undefined ? col : 0);
55562         var cm = this.grid.colModel;
55563         while(cm.isHidden(col)){
55564             col++;
55565         }
55566
55567         var el = this.getCell(row, col);
55568         if(!el){
55569             return null;
55570         }
55571         var c = this.scroller.dom;
55572
55573         var ctop = parseInt(el.offsetTop, 10);
55574         var cleft = parseInt(el.offsetLeft, 10);
55575         var cbot = ctop + el.offsetHeight;
55576         var cright = cleft + el.offsetWidth;
55577         
55578         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55579         var stop = parseInt(c.scrollTop, 10);
55580         var sleft = parseInt(c.scrollLeft, 10);
55581         var sbot = stop + ch;
55582         var sright = sleft + c.clientWidth;
55583         /*
55584         Roo.log('GridView.ensureVisible:' +
55585                 ' ctop:' + ctop +
55586                 ' c.clientHeight:' + c.clientHeight +
55587                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55588                 ' stop:' + stop +
55589                 ' cbot:' + cbot +
55590                 ' sbot:' + sbot +
55591                 ' ch:' + ch  
55592                 );
55593         */
55594         if(ctop < stop){
55595              c.scrollTop = ctop;
55596             //Roo.log("set scrolltop to ctop DISABLE?");
55597         }else if(cbot > sbot){
55598             //Roo.log("set scrolltop to cbot-ch");
55599             c.scrollTop = cbot-ch;
55600         }
55601         
55602         if(hscroll !== false){
55603             if(cleft < sleft){
55604                 c.scrollLeft = cleft;
55605             }else if(cright > sright){
55606                 c.scrollLeft = cright-c.clientWidth;
55607             }
55608         }
55609          
55610         return el;
55611     },
55612
55613     updateColumns : function(){
55614         this.grid.stopEditing();
55615         var cm = this.grid.colModel, colIds = this.getColumnIds();
55616         //var totalWidth = cm.getTotalWidth();
55617         var pos = 0;
55618         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55619             //if(cm.isHidden(i)) continue;
55620             var w = cm.getColumnWidth(i);
55621             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55622             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55623         }
55624         this.updateSplitters();
55625     },
55626
55627     generateRules : function(cm){
55628         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55629         Roo.util.CSS.removeStyleSheet(rulesId);
55630         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55631             var cid = cm.getColumnId(i);
55632             var align = '';
55633             if(cm.config[i].align){
55634                 align = 'text-align:'+cm.config[i].align+';';
55635             }
55636             var hidden = '';
55637             if(cm.isHidden(i)){
55638                 hidden = 'display:none;';
55639             }
55640             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55641             ruleBuf.push(
55642                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55643                     this.hdSelector, cid, " {\n", align, width, "}\n",
55644                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55645                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55646         }
55647         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55648     },
55649
55650     updateSplitters : function(){
55651         var cm = this.cm, s = this.getSplitters();
55652         if(s){ // splitters not created yet
55653             var pos = 0, locked = true;
55654             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55655                 if(cm.isHidden(i)) {
55656                     continue;
55657                 }
55658                 var w = cm.getColumnWidth(i); // make sure it's a number
55659                 if(!cm.isLocked(i) && locked){
55660                     pos = 0;
55661                     locked = false;
55662                 }
55663                 pos += w;
55664                 s[i].style.left = (pos-this.splitOffset) + "px";
55665             }
55666         }
55667     },
55668
55669     handleHiddenChange : function(colModel, colIndex, hidden){
55670         if(hidden){
55671             this.hideColumn(colIndex);
55672         }else{
55673             this.unhideColumn(colIndex);
55674         }
55675     },
55676
55677     hideColumn : function(colIndex){
55678         var cid = this.getColumnId(colIndex);
55679         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55680         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55681         if(Roo.isSafari){
55682             this.updateHeaders();
55683         }
55684         this.updateSplitters();
55685         this.layout();
55686     },
55687
55688     unhideColumn : function(colIndex){
55689         var cid = this.getColumnId(colIndex);
55690         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55691         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55692
55693         if(Roo.isSafari){
55694             this.updateHeaders();
55695         }
55696         this.updateSplitters();
55697         this.layout();
55698     },
55699
55700     insertRows : function(dm, firstRow, lastRow, isUpdate){
55701         if(firstRow == 0 && lastRow == dm.getCount()-1){
55702             this.refresh();
55703         }else{
55704             if(!isUpdate){
55705                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55706             }
55707             var s = this.getScrollState();
55708             var markup = this.renderRows(firstRow, lastRow);
55709             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55710             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55711             this.restoreScroll(s);
55712             if(!isUpdate){
55713                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55714                 this.syncRowHeights(firstRow, lastRow);
55715                 this.stripeRows(firstRow);
55716                 this.layout();
55717             }
55718         }
55719     },
55720
55721     bufferRows : function(markup, target, index){
55722         var before = null, trows = target.rows, tbody = target.tBodies[0];
55723         if(index < trows.length){
55724             before = trows[index];
55725         }
55726         var b = document.createElement("div");
55727         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55728         var rows = b.firstChild.rows;
55729         for(var i = 0, len = rows.length; i < len; i++){
55730             if(before){
55731                 tbody.insertBefore(rows[0], before);
55732             }else{
55733                 tbody.appendChild(rows[0]);
55734             }
55735         }
55736         b.innerHTML = "";
55737         b = null;
55738     },
55739
55740     deleteRows : function(dm, firstRow, lastRow){
55741         if(dm.getRowCount()<1){
55742             this.fireEvent("beforerefresh", this);
55743             this.mainBody.update("");
55744             this.lockedBody.update("");
55745             this.fireEvent("refresh", this);
55746         }else{
55747             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55748             var bt = this.getBodyTable();
55749             var tbody = bt.firstChild;
55750             var rows = bt.rows;
55751             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55752                 tbody.removeChild(rows[firstRow]);
55753             }
55754             this.stripeRows(firstRow);
55755             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55756         }
55757     },
55758
55759     updateRows : function(dataSource, firstRow, lastRow){
55760         var s = this.getScrollState();
55761         this.refresh();
55762         this.restoreScroll(s);
55763     },
55764
55765     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55766         if(!noRefresh){
55767            this.refresh();
55768         }
55769         this.updateHeaderSortState();
55770     },
55771
55772     getScrollState : function(){
55773         
55774         var sb = this.scroller.dom;
55775         return {left: sb.scrollLeft, top: sb.scrollTop};
55776     },
55777
55778     stripeRows : function(startRow){
55779         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55780             return;
55781         }
55782         startRow = startRow || 0;
55783         var rows = this.getBodyTable().rows;
55784         var lrows = this.getLockedTable().rows;
55785         var cls = ' x-grid-row-alt ';
55786         for(var i = startRow, len = rows.length; i < len; i++){
55787             var row = rows[i], lrow = lrows[i];
55788             var isAlt = ((i+1) % 2 == 0);
55789             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55790             if(isAlt == hasAlt){
55791                 continue;
55792             }
55793             if(isAlt){
55794                 row.className += " x-grid-row-alt";
55795             }else{
55796                 row.className = row.className.replace("x-grid-row-alt", "");
55797             }
55798             if(lrow){
55799                 lrow.className = row.className;
55800             }
55801         }
55802     },
55803
55804     restoreScroll : function(state){
55805         //Roo.log('GridView.restoreScroll');
55806         var sb = this.scroller.dom;
55807         sb.scrollLeft = state.left;
55808         sb.scrollTop = state.top;
55809         this.syncScroll();
55810     },
55811
55812     syncScroll : function(){
55813         //Roo.log('GridView.syncScroll');
55814         var sb = this.scroller.dom;
55815         var sh = this.mainHd.dom;
55816         var bs = this.mainBody.dom;
55817         var lv = this.lockedBody.dom;
55818         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55819         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55820     },
55821
55822     handleScroll : function(e){
55823         this.syncScroll();
55824         var sb = this.scroller.dom;
55825         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55826         e.stopEvent();
55827     },
55828
55829     handleWheel : function(e){
55830         var d = e.getWheelDelta();
55831         this.scroller.dom.scrollTop -= d*22;
55832         // set this here to prevent jumpy scrolling on large tables
55833         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55834         e.stopEvent();
55835     },
55836
55837     renderRows : function(startRow, endRow){
55838         // pull in all the crap needed to render rows
55839         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55840         var colCount = cm.getColumnCount();
55841
55842         if(ds.getCount() < 1){
55843             return ["", ""];
55844         }
55845
55846         // build a map for all the columns
55847         var cs = [];
55848         for(var i = 0; i < colCount; i++){
55849             var name = cm.getDataIndex(i);
55850             cs[i] = {
55851                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55852                 renderer : cm.getRenderer(i),
55853                 id : cm.getColumnId(i),
55854                 locked : cm.isLocked(i),
55855                 has_editor : cm.isCellEditable(i)
55856             };
55857         }
55858
55859         startRow = startRow || 0;
55860         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55861
55862         // records to render
55863         var rs = ds.getRange(startRow, endRow);
55864
55865         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55866     },
55867
55868     // As much as I hate to duplicate code, this was branched because FireFox really hates
55869     // [].join("") on strings. The performance difference was substantial enough to
55870     // branch this function
55871     doRender : Roo.isGecko ?
55872             function(cs, rs, ds, startRow, colCount, stripe){
55873                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55874                 // buffers
55875                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55876                 
55877                 var hasListener = this.grid.hasListener('rowclass');
55878                 var rowcfg = {};
55879                 for(var j = 0, len = rs.length; j < len; j++){
55880                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55881                     for(var i = 0; i < colCount; i++){
55882                         c = cs[i];
55883                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55884                         p.id = c.id;
55885                         p.css = p.attr = "";
55886                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55887                         if(p.value == undefined || p.value === "") {
55888                             p.value = "&#160;";
55889                         }
55890                         if(c.has_editor){
55891                             p.css += ' x-grid-editable-cell';
55892                         }
55893                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55894                             p.css +=  ' x-grid-dirty-cell';
55895                         }
55896                         var markup = ct.apply(p);
55897                         if(!c.locked){
55898                             cb+= markup;
55899                         }else{
55900                             lcb+= markup;
55901                         }
55902                     }
55903                     var alt = [];
55904                     if(stripe && ((rowIndex+1) % 2 == 0)){
55905                         alt.push("x-grid-row-alt")
55906                     }
55907                     if(r.dirty){
55908                         alt.push(  " x-grid-dirty-row");
55909                     }
55910                     rp.cells = lcb;
55911                     if(this.getRowClass){
55912                         alt.push(this.getRowClass(r, rowIndex));
55913                     }
55914                     if (hasListener) {
55915                         rowcfg = {
55916                              
55917                             record: r,
55918                             rowIndex : rowIndex,
55919                             rowClass : ''
55920                         };
55921                         this.grid.fireEvent('rowclass', this, rowcfg);
55922                         alt.push(rowcfg.rowClass);
55923                     }
55924                     rp.alt = alt.join(" ");
55925                     lbuf+= rt.apply(rp);
55926                     rp.cells = cb;
55927                     buf+=  rt.apply(rp);
55928                 }
55929                 return [lbuf, buf];
55930             } :
55931             function(cs, rs, ds, startRow, colCount, stripe){
55932                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55933                 // buffers
55934                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55935                 var hasListener = this.grid.hasListener('rowclass');
55936  
55937                 var rowcfg = {};
55938                 for(var j = 0, len = rs.length; j < len; j++){
55939                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55940                     for(var i = 0; i < colCount; i++){
55941                         c = cs[i];
55942                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55943                         p.id = c.id;
55944                         p.css = p.attr = "";
55945                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55946                         if(p.value == undefined || p.value === "") {
55947                             p.value = "&#160;";
55948                         }
55949                         //Roo.log(c);
55950                          if(c.has_editor){
55951                             p.css += ' x-grid-editable-cell';
55952                         }
55953                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55954                             p.css += ' x-grid-dirty-cell' 
55955                         }
55956                         
55957                         var markup = ct.apply(p);
55958                         if(!c.locked){
55959                             cb[cb.length] = markup;
55960                         }else{
55961                             lcb[lcb.length] = markup;
55962                         }
55963                     }
55964                     var alt = [];
55965                     if(stripe && ((rowIndex+1) % 2 == 0)){
55966                         alt.push( "x-grid-row-alt");
55967                     }
55968                     if(r.dirty){
55969                         alt.push(" x-grid-dirty-row");
55970                     }
55971                     rp.cells = lcb;
55972                     if(this.getRowClass){
55973                         alt.push( this.getRowClass(r, rowIndex));
55974                     }
55975                     if (hasListener) {
55976                         rowcfg = {
55977                              
55978                             record: r,
55979                             rowIndex : rowIndex,
55980                             rowClass : ''
55981                         };
55982                         this.grid.fireEvent('rowclass', this, rowcfg);
55983                         alt.push(rowcfg.rowClass);
55984                     }
55985                     
55986                     rp.alt = alt.join(" ");
55987                     rp.cells = lcb.join("");
55988                     lbuf[lbuf.length] = rt.apply(rp);
55989                     rp.cells = cb.join("");
55990                     buf[buf.length] =  rt.apply(rp);
55991                 }
55992                 return [lbuf.join(""), buf.join("")];
55993             },
55994
55995     renderBody : function(){
55996         var markup = this.renderRows();
55997         var bt = this.templates.body;
55998         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
55999     },
56000
56001     /**
56002      * Refreshes the grid
56003      * @param {Boolean} headersToo
56004      */
56005     refresh : function(headersToo){
56006         this.fireEvent("beforerefresh", this);
56007         this.grid.stopEditing();
56008         var result = this.renderBody();
56009         this.lockedBody.update(result[0]);
56010         this.mainBody.update(result[1]);
56011         if(headersToo === true){
56012             this.updateHeaders();
56013             this.updateColumns();
56014             this.updateSplitters();
56015             this.updateHeaderSortState();
56016         }
56017         this.syncRowHeights();
56018         this.layout();
56019         this.fireEvent("refresh", this);
56020     },
56021
56022     handleColumnMove : function(cm, oldIndex, newIndex){
56023         this.indexMap = null;
56024         var s = this.getScrollState();
56025         this.refresh(true);
56026         this.restoreScroll(s);
56027         this.afterMove(newIndex);
56028     },
56029
56030     afterMove : function(colIndex){
56031         if(this.enableMoveAnim && Roo.enableFx){
56032             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56033         }
56034         // if multisort - fix sortOrder, and reload..
56035         if (this.grid.dataSource.multiSort) {
56036             // the we can call sort again..
56037             var dm = this.grid.dataSource;
56038             var cm = this.grid.colModel;
56039             var so = [];
56040             for(var i = 0; i < cm.config.length; i++ ) {
56041                 
56042                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56043                     continue; // dont' bother, it's not in sort list or being set.
56044                 }
56045                 
56046                 so.push(cm.config[i].dataIndex);
56047             };
56048             dm.sortOrder = so;
56049             dm.load(dm.lastOptions);
56050             
56051             
56052         }
56053         
56054     },
56055
56056     updateCell : function(dm, rowIndex, dataIndex){
56057         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56058         if(typeof colIndex == "undefined"){ // not present in grid
56059             return;
56060         }
56061         var cm = this.grid.colModel;
56062         var cell = this.getCell(rowIndex, colIndex);
56063         var cellText = this.getCellText(rowIndex, colIndex);
56064
56065         var p = {
56066             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56067             id : cm.getColumnId(colIndex),
56068             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56069         };
56070         var renderer = cm.getRenderer(colIndex);
56071         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56072         if(typeof val == "undefined" || val === "") {
56073             val = "&#160;";
56074         }
56075         cellText.innerHTML = val;
56076         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56077         this.syncRowHeights(rowIndex, rowIndex);
56078     },
56079
56080     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56081         var maxWidth = 0;
56082         if(this.grid.autoSizeHeaders){
56083             var h = this.getHeaderCellMeasure(colIndex);
56084             maxWidth = Math.max(maxWidth, h.scrollWidth);
56085         }
56086         var tb, index;
56087         if(this.cm.isLocked(colIndex)){
56088             tb = this.getLockedTable();
56089             index = colIndex;
56090         }else{
56091             tb = this.getBodyTable();
56092             index = colIndex - this.cm.getLockedCount();
56093         }
56094         if(tb && tb.rows){
56095             var rows = tb.rows;
56096             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56097             for(var i = 0; i < stopIndex; i++){
56098                 var cell = rows[i].childNodes[index].firstChild;
56099                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56100             }
56101         }
56102         return maxWidth + /*margin for error in IE*/ 5;
56103     },
56104     /**
56105      * Autofit a column to its content.
56106      * @param {Number} colIndex
56107      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56108      */
56109      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56110          if(this.cm.isHidden(colIndex)){
56111              return; // can't calc a hidden column
56112          }
56113         if(forceMinSize){
56114             var cid = this.cm.getColumnId(colIndex);
56115             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56116            if(this.grid.autoSizeHeaders){
56117                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56118            }
56119         }
56120         var newWidth = this.calcColumnWidth(colIndex);
56121         this.cm.setColumnWidth(colIndex,
56122             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56123         if(!suppressEvent){
56124             this.grid.fireEvent("columnresize", colIndex, newWidth);
56125         }
56126     },
56127
56128     /**
56129      * Autofits all columns to their content and then expands to fit any extra space in the grid
56130      */
56131      autoSizeColumns : function(){
56132         var cm = this.grid.colModel;
56133         var colCount = cm.getColumnCount();
56134         for(var i = 0; i < colCount; i++){
56135             this.autoSizeColumn(i, true, true);
56136         }
56137         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56138             this.fitColumns();
56139         }else{
56140             this.updateColumns();
56141             this.layout();
56142         }
56143     },
56144
56145     /**
56146      * Autofits all columns to the grid's width proportionate with their current size
56147      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56148      */
56149     fitColumns : function(reserveScrollSpace){
56150         var cm = this.grid.colModel;
56151         var colCount = cm.getColumnCount();
56152         var cols = [];
56153         var width = 0;
56154         var i, w;
56155         for (i = 0; i < colCount; i++){
56156             if(!cm.isHidden(i) && !cm.isFixed(i)){
56157                 w = cm.getColumnWidth(i);
56158                 cols.push(i);
56159                 cols.push(w);
56160                 width += w;
56161             }
56162         }
56163         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56164         if(reserveScrollSpace){
56165             avail -= 17;
56166         }
56167         var frac = (avail - cm.getTotalWidth())/width;
56168         while (cols.length){
56169             w = cols.pop();
56170             i = cols.pop();
56171             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56172         }
56173         this.updateColumns();
56174         this.layout();
56175     },
56176
56177     onRowSelect : function(rowIndex){
56178         var row = this.getRowComposite(rowIndex);
56179         row.addClass("x-grid-row-selected");
56180     },
56181
56182     onRowDeselect : function(rowIndex){
56183         var row = this.getRowComposite(rowIndex);
56184         row.removeClass("x-grid-row-selected");
56185     },
56186
56187     onCellSelect : function(row, col){
56188         var cell = this.getCell(row, col);
56189         if(cell){
56190             Roo.fly(cell).addClass("x-grid-cell-selected");
56191         }
56192     },
56193
56194     onCellDeselect : function(row, col){
56195         var cell = this.getCell(row, col);
56196         if(cell){
56197             Roo.fly(cell).removeClass("x-grid-cell-selected");
56198         }
56199     },
56200
56201     updateHeaderSortState : function(){
56202         
56203         // sort state can be single { field: xxx, direction : yyy}
56204         // or   { xxx=>ASC , yyy : DESC ..... }
56205         
56206         var mstate = {};
56207         if (!this.ds.multiSort) { 
56208             var state = this.ds.getSortState();
56209             if(!state){
56210                 return;
56211             }
56212             mstate[state.field] = state.direction;
56213             // FIXME... - this is not used here.. but might be elsewhere..
56214             this.sortState = state;
56215             
56216         } else {
56217             mstate = this.ds.sortToggle;
56218         }
56219         //remove existing sort classes..
56220         
56221         var sc = this.sortClasses;
56222         var hds = this.el.select(this.headerSelector).removeClass(sc);
56223         
56224         for(var f in mstate) {
56225         
56226             var sortColumn = this.cm.findColumnIndex(f);
56227             
56228             if(sortColumn != -1){
56229                 var sortDir = mstate[f];        
56230                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56231             }
56232         }
56233         
56234          
56235         
56236     },
56237
56238
56239     handleHeaderClick : function(g, index,e){
56240         
56241         Roo.log("header click");
56242         
56243         if (Roo.isTouch) {
56244             // touch events on header are handled by context
56245             this.handleHdCtx(g,index,e);
56246             return;
56247         }
56248         
56249         
56250         if(this.headersDisabled){
56251             return;
56252         }
56253         var dm = g.dataSource, cm = g.colModel;
56254         if(!cm.isSortable(index)){
56255             return;
56256         }
56257         g.stopEditing();
56258         
56259         if (dm.multiSort) {
56260             // update the sortOrder
56261             var so = [];
56262             for(var i = 0; i < cm.config.length; i++ ) {
56263                 
56264                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56265                     continue; // dont' bother, it's not in sort list or being set.
56266                 }
56267                 
56268                 so.push(cm.config[i].dataIndex);
56269             };
56270             dm.sortOrder = so;
56271         }
56272         
56273         
56274         dm.sort(cm.getDataIndex(index));
56275     },
56276
56277
56278     destroy : function(){
56279         if(this.colMenu){
56280             this.colMenu.removeAll();
56281             Roo.menu.MenuMgr.unregister(this.colMenu);
56282             this.colMenu.getEl().remove();
56283             delete this.colMenu;
56284         }
56285         if(this.hmenu){
56286             this.hmenu.removeAll();
56287             Roo.menu.MenuMgr.unregister(this.hmenu);
56288             this.hmenu.getEl().remove();
56289             delete this.hmenu;
56290         }
56291         if(this.grid.enableColumnMove){
56292             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56293             if(dds){
56294                 for(var dd in dds){
56295                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56296                         var elid = dds[dd].dragElId;
56297                         dds[dd].unreg();
56298                         Roo.get(elid).remove();
56299                     } else if(dds[dd].config.isTarget){
56300                         dds[dd].proxyTop.remove();
56301                         dds[dd].proxyBottom.remove();
56302                         dds[dd].unreg();
56303                     }
56304                     if(Roo.dd.DDM.locationCache[dd]){
56305                         delete Roo.dd.DDM.locationCache[dd];
56306                     }
56307                 }
56308                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56309             }
56310         }
56311         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56312         this.bind(null, null);
56313         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56314     },
56315
56316     handleLockChange : function(){
56317         this.refresh(true);
56318     },
56319
56320     onDenyColumnLock : function(){
56321
56322     },
56323
56324     onDenyColumnHide : function(){
56325
56326     },
56327
56328     handleHdMenuClick : function(item){
56329         var index = this.hdCtxIndex;
56330         var cm = this.cm, ds = this.ds;
56331         switch(item.id){
56332             case "asc":
56333                 ds.sort(cm.getDataIndex(index), "ASC");
56334                 break;
56335             case "desc":
56336                 ds.sort(cm.getDataIndex(index), "DESC");
56337                 break;
56338             case "lock":
56339                 var lc = cm.getLockedCount();
56340                 if(cm.getColumnCount(true) <= lc+1){
56341                     this.onDenyColumnLock();
56342                     return;
56343                 }
56344                 if(lc != index){
56345                     cm.setLocked(index, true, true);
56346                     cm.moveColumn(index, lc);
56347                     this.grid.fireEvent("columnmove", index, lc);
56348                 }else{
56349                     cm.setLocked(index, true);
56350                 }
56351             break;
56352             case "unlock":
56353                 var lc = cm.getLockedCount();
56354                 if((lc-1) != index){
56355                     cm.setLocked(index, false, true);
56356                     cm.moveColumn(index, lc-1);
56357                     this.grid.fireEvent("columnmove", index, lc-1);
56358                 }else{
56359                     cm.setLocked(index, false);
56360                 }
56361             break;
56362             case 'wider': // used to expand cols on touch..
56363             case 'narrow':
56364                 var cw = cm.getColumnWidth(index);
56365                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56366                 cw = Math.max(0, cw);
56367                 cw = Math.min(cw,4000);
56368                 cm.setColumnWidth(index, cw);
56369                 break;
56370                 
56371             default:
56372                 index = cm.getIndexById(item.id.substr(4));
56373                 if(index != -1){
56374                     if(item.checked && cm.getColumnCount(true) <= 1){
56375                         this.onDenyColumnHide();
56376                         return false;
56377                     }
56378                     cm.setHidden(index, item.checked);
56379                 }
56380         }
56381         return true;
56382     },
56383
56384     beforeColMenuShow : function(){
56385         var cm = this.cm,  colCount = cm.getColumnCount();
56386         this.colMenu.removeAll();
56387         for(var i = 0; i < colCount; i++){
56388             this.colMenu.add(new Roo.menu.CheckItem({
56389                 id: "col-"+cm.getColumnId(i),
56390                 text: cm.getColumnHeader(i),
56391                 checked: !cm.isHidden(i),
56392                 hideOnClick:false
56393             }));
56394         }
56395     },
56396
56397     handleHdCtx : function(g, index, e){
56398         e.stopEvent();
56399         var hd = this.getHeaderCell(index);
56400         this.hdCtxIndex = index;
56401         var ms = this.hmenu.items, cm = this.cm;
56402         ms.get("asc").setDisabled(!cm.isSortable(index));
56403         ms.get("desc").setDisabled(!cm.isSortable(index));
56404         if(this.grid.enableColLock !== false){
56405             ms.get("lock").setDisabled(cm.isLocked(index));
56406             ms.get("unlock").setDisabled(!cm.isLocked(index));
56407         }
56408         this.hmenu.show(hd, "tl-bl");
56409     },
56410
56411     handleHdOver : function(e){
56412         var hd = this.findHeaderCell(e.getTarget());
56413         if(hd && !this.headersDisabled){
56414             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56415                this.fly(hd).addClass("x-grid-hd-over");
56416             }
56417         }
56418     },
56419
56420     handleHdOut : function(e){
56421         var hd = this.findHeaderCell(e.getTarget());
56422         if(hd){
56423             this.fly(hd).removeClass("x-grid-hd-over");
56424         }
56425     },
56426
56427     handleSplitDblClick : function(e, t){
56428         var i = this.getCellIndex(t);
56429         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56430             this.autoSizeColumn(i, true);
56431             this.layout();
56432         }
56433     },
56434
56435     render : function(){
56436
56437         var cm = this.cm;
56438         var colCount = cm.getColumnCount();
56439
56440         if(this.grid.monitorWindowResize === true){
56441             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56442         }
56443         var header = this.renderHeaders();
56444         var body = this.templates.body.apply({rows:""});
56445         var html = this.templates.master.apply({
56446             lockedBody: body,
56447             body: body,
56448             lockedHeader: header[0],
56449             header: header[1]
56450         });
56451
56452         //this.updateColumns();
56453
56454         this.grid.getGridEl().dom.innerHTML = html;
56455
56456         this.initElements();
56457         
56458         // a kludge to fix the random scolling effect in webkit
56459         this.el.on("scroll", function() {
56460             this.el.dom.scrollTop=0; // hopefully not recursive..
56461         },this);
56462
56463         this.scroller.on("scroll", this.handleScroll, this);
56464         this.lockedBody.on("mousewheel", this.handleWheel, this);
56465         this.mainBody.on("mousewheel", this.handleWheel, this);
56466
56467         this.mainHd.on("mouseover", this.handleHdOver, this);
56468         this.mainHd.on("mouseout", this.handleHdOut, this);
56469         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56470                 {delegate: "."+this.splitClass});
56471
56472         this.lockedHd.on("mouseover", this.handleHdOver, this);
56473         this.lockedHd.on("mouseout", this.handleHdOut, this);
56474         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56475                 {delegate: "."+this.splitClass});
56476
56477         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56478             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56479         }
56480
56481         this.updateSplitters();
56482
56483         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56484             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56485             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56486         }
56487
56488         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56489             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56490             this.hmenu.add(
56491                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56492                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56493             );
56494             if(this.grid.enableColLock !== false){
56495                 this.hmenu.add('-',
56496                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56497                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56498                 );
56499             }
56500             if (Roo.isTouch) {
56501                  this.hmenu.add('-',
56502                     {id:"wider", text: this.columnsWiderText},
56503                     {id:"narrow", text: this.columnsNarrowText }
56504                 );
56505                 
56506                  
56507             }
56508             
56509             if(this.grid.enableColumnHide !== false){
56510
56511                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56512                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56513                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56514
56515                 this.hmenu.add('-',
56516                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56517                 );
56518             }
56519             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56520
56521             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56522         }
56523
56524         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56525             this.dd = new Roo.grid.GridDragZone(this.grid, {
56526                 ddGroup : this.grid.ddGroup || 'GridDD'
56527             });
56528             
56529         }
56530
56531         /*
56532         for(var i = 0; i < colCount; i++){
56533             if(cm.isHidden(i)){
56534                 this.hideColumn(i);
56535             }
56536             if(cm.config[i].align){
56537                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56538                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56539             }
56540         }*/
56541         
56542         this.updateHeaderSortState();
56543
56544         this.beforeInitialResize();
56545         this.layout(true);
56546
56547         // two part rendering gives faster view to the user
56548         this.renderPhase2.defer(1, this);
56549     },
56550
56551     renderPhase2 : function(){
56552         // render the rows now
56553         this.refresh();
56554         if(this.grid.autoSizeColumns){
56555             this.autoSizeColumns();
56556         }
56557     },
56558
56559     beforeInitialResize : function(){
56560
56561     },
56562
56563     onColumnSplitterMoved : function(i, w){
56564         this.userResized = true;
56565         var cm = this.grid.colModel;
56566         cm.setColumnWidth(i, w, true);
56567         var cid = cm.getColumnId(i);
56568         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56569         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56570         this.updateSplitters();
56571         this.layout();
56572         this.grid.fireEvent("columnresize", i, w);
56573     },
56574
56575     syncRowHeights : function(startIndex, endIndex){
56576         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56577             startIndex = startIndex || 0;
56578             var mrows = this.getBodyTable().rows;
56579             var lrows = this.getLockedTable().rows;
56580             var len = mrows.length-1;
56581             endIndex = Math.min(endIndex || len, len);
56582             for(var i = startIndex; i <= endIndex; i++){
56583                 var m = mrows[i], l = lrows[i];
56584                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56585                 m.style.height = l.style.height = h + "px";
56586             }
56587         }
56588     },
56589
56590     layout : function(initialRender, is2ndPass){
56591         var g = this.grid;
56592         var auto = g.autoHeight;
56593         var scrollOffset = 16;
56594         var c = g.getGridEl(), cm = this.cm,
56595                 expandCol = g.autoExpandColumn,
56596                 gv = this;
56597         //c.beginMeasure();
56598
56599         if(!c.dom.offsetWidth){ // display:none?
56600             if(initialRender){
56601                 this.lockedWrap.show();
56602                 this.mainWrap.show();
56603             }
56604             return;
56605         }
56606
56607         var hasLock = this.cm.isLocked(0);
56608
56609         var tbh = this.headerPanel.getHeight();
56610         var bbh = this.footerPanel.getHeight();
56611
56612         if(auto){
56613             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56614             var newHeight = ch + c.getBorderWidth("tb");
56615             if(g.maxHeight){
56616                 newHeight = Math.min(g.maxHeight, newHeight);
56617             }
56618             c.setHeight(newHeight);
56619         }
56620
56621         if(g.autoWidth){
56622             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56623         }
56624
56625         var s = this.scroller;
56626
56627         var csize = c.getSize(true);
56628
56629         this.el.setSize(csize.width, csize.height);
56630
56631         this.headerPanel.setWidth(csize.width);
56632         this.footerPanel.setWidth(csize.width);
56633
56634         var hdHeight = this.mainHd.getHeight();
56635         var vw = csize.width;
56636         var vh = csize.height - (tbh + bbh);
56637
56638         s.setSize(vw, vh);
56639
56640         var bt = this.getBodyTable();
56641         
56642         if(cm.getLockedCount() == cm.config.length){
56643             bt = this.getLockedTable();
56644         }
56645         
56646         var ltWidth = hasLock ?
56647                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56648
56649         var scrollHeight = bt.offsetHeight;
56650         var scrollWidth = ltWidth + bt.offsetWidth;
56651         var vscroll = false, hscroll = false;
56652
56653         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56654
56655         var lw = this.lockedWrap, mw = this.mainWrap;
56656         var lb = this.lockedBody, mb = this.mainBody;
56657
56658         setTimeout(function(){
56659             var t = s.dom.offsetTop;
56660             var w = s.dom.clientWidth,
56661                 h = s.dom.clientHeight;
56662
56663             lw.setTop(t);
56664             lw.setSize(ltWidth, h);
56665
56666             mw.setLeftTop(ltWidth, t);
56667             mw.setSize(w-ltWidth, h);
56668
56669             lb.setHeight(h-hdHeight);
56670             mb.setHeight(h-hdHeight);
56671
56672             if(is2ndPass !== true && !gv.userResized && expandCol){
56673                 // high speed resize without full column calculation
56674                 
56675                 var ci = cm.getIndexById(expandCol);
56676                 if (ci < 0) {
56677                     ci = cm.findColumnIndex(expandCol);
56678                 }
56679                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56680                 var expandId = cm.getColumnId(ci);
56681                 var  tw = cm.getTotalWidth(false);
56682                 var currentWidth = cm.getColumnWidth(ci);
56683                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56684                 if(currentWidth != cw){
56685                     cm.setColumnWidth(ci, cw, true);
56686                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56687                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56688                     gv.updateSplitters();
56689                     gv.layout(false, true);
56690                 }
56691             }
56692
56693             if(initialRender){
56694                 lw.show();
56695                 mw.show();
56696             }
56697             //c.endMeasure();
56698         }, 10);
56699     },
56700
56701     onWindowResize : function(){
56702         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56703             return;
56704         }
56705         this.layout();
56706     },
56707
56708     appendFooter : function(parentEl){
56709         return null;
56710     },
56711
56712     sortAscText : "Sort Ascending",
56713     sortDescText : "Sort Descending",
56714     lockText : "Lock Column",
56715     unlockText : "Unlock Column",
56716     columnsText : "Columns",
56717  
56718     columnsWiderText : "Wider",
56719     columnsNarrowText : "Thinner"
56720 });
56721
56722
56723 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56724     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56725     this.proxy.el.addClass('x-grid3-col-dd');
56726 };
56727
56728 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56729     handleMouseDown : function(e){
56730
56731     },
56732
56733     callHandleMouseDown : function(e){
56734         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56735     }
56736 });
56737 /*
56738  * Based on:
56739  * Ext JS Library 1.1.1
56740  * Copyright(c) 2006-2007, Ext JS, LLC.
56741  *
56742  * Originally Released Under LGPL - original licence link has changed is not relivant.
56743  *
56744  * Fork - LGPL
56745  * <script type="text/javascript">
56746  */
56747  
56748 // private
56749 // This is a support class used internally by the Grid components
56750 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56751     this.grid = grid;
56752     this.view = grid.getView();
56753     this.proxy = this.view.resizeProxy;
56754     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56755         "gridSplitters" + this.grid.getGridEl().id, {
56756         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56757     });
56758     this.setHandleElId(Roo.id(hd));
56759     this.setOuterHandleElId(Roo.id(hd2));
56760     this.scroll = false;
56761 };
56762 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56763     fly: Roo.Element.fly,
56764
56765     b4StartDrag : function(x, y){
56766         this.view.headersDisabled = true;
56767         this.proxy.setHeight(this.view.mainWrap.getHeight());
56768         var w = this.cm.getColumnWidth(this.cellIndex);
56769         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56770         this.resetConstraints();
56771         this.setXConstraint(minw, 1000);
56772         this.setYConstraint(0, 0);
56773         this.minX = x - minw;
56774         this.maxX = x + 1000;
56775         this.startPos = x;
56776         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56777     },
56778
56779
56780     handleMouseDown : function(e){
56781         ev = Roo.EventObject.setEvent(e);
56782         var t = this.fly(ev.getTarget());
56783         if(t.hasClass("x-grid-split")){
56784             this.cellIndex = this.view.getCellIndex(t.dom);
56785             this.split = t.dom;
56786             this.cm = this.grid.colModel;
56787             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56788                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56789             }
56790         }
56791     },
56792
56793     endDrag : function(e){
56794         this.view.headersDisabled = false;
56795         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56796         var diff = endX - this.startPos;
56797         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56798     },
56799
56800     autoOffset : function(){
56801         this.setDelta(0,0);
56802     }
56803 });/*
56804  * Based on:
56805  * Ext JS Library 1.1.1
56806  * Copyright(c) 2006-2007, Ext JS, LLC.
56807  *
56808  * Originally Released Under LGPL - original licence link has changed is not relivant.
56809  *
56810  * Fork - LGPL
56811  * <script type="text/javascript">
56812  */
56813  
56814 // private
56815 // This is a support class used internally by the Grid components
56816 Roo.grid.GridDragZone = function(grid, config){
56817     this.view = grid.getView();
56818     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56819     if(this.view.lockedBody){
56820         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56821         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56822     }
56823     this.scroll = false;
56824     this.grid = grid;
56825     this.ddel = document.createElement('div');
56826     this.ddel.className = 'x-grid-dd-wrap';
56827 };
56828
56829 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56830     ddGroup : "GridDD",
56831
56832     getDragData : function(e){
56833         var t = Roo.lib.Event.getTarget(e);
56834         var rowIndex = this.view.findRowIndex(t);
56835         var sm = this.grid.selModel;
56836             
56837         //Roo.log(rowIndex);
56838         
56839         if (sm.getSelectedCell) {
56840             // cell selection..
56841             if (!sm.getSelectedCell()) {
56842                 return false;
56843             }
56844             if (rowIndex != sm.getSelectedCell()[0]) {
56845                 return false;
56846             }
56847         
56848         }
56849         
56850         if(rowIndex !== false){
56851             
56852             // if editorgrid.. 
56853             
56854             
56855             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56856                
56857             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56858               //  
56859             //}
56860             if (e.hasModifier()){
56861                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56862             }
56863             
56864             Roo.log("getDragData");
56865             
56866             return {
56867                 grid: this.grid,
56868                 ddel: this.ddel,
56869                 rowIndex: rowIndex,
56870                 selections:sm.getSelections ? sm.getSelections() : (
56871                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56872                 )
56873             };
56874         }
56875         return false;
56876     },
56877
56878     onInitDrag : function(e){
56879         var data = this.dragData;
56880         this.ddel.innerHTML = this.grid.getDragDropText();
56881         this.proxy.update(this.ddel);
56882         // fire start drag?
56883     },
56884
56885     afterRepair : function(){
56886         this.dragging = false;
56887     },
56888
56889     getRepairXY : function(e, data){
56890         return false;
56891     },
56892
56893     onEndDrag : function(data, e){
56894         // fire end drag?
56895     },
56896
56897     onValidDrop : function(dd, e, id){
56898         // fire drag drop?
56899         this.hideProxy();
56900     },
56901
56902     beforeInvalidDrop : function(e, id){
56903
56904     }
56905 });/*
56906  * Based on:
56907  * Ext JS Library 1.1.1
56908  * Copyright(c) 2006-2007, Ext JS, LLC.
56909  *
56910  * Originally Released Under LGPL - original licence link has changed is not relivant.
56911  *
56912  * Fork - LGPL
56913  * <script type="text/javascript">
56914  */
56915  
56916
56917 /**
56918  * @class Roo.grid.ColumnModel
56919  * @extends Roo.util.Observable
56920  * This is the default implementation of a ColumnModel used by the Grid. It defines
56921  * the columns in the grid.
56922  * <br>Usage:<br>
56923  <pre><code>
56924  var colModel = new Roo.grid.ColumnModel([
56925         {header: "Ticker", width: 60, sortable: true, locked: true},
56926         {header: "Company Name", width: 150, sortable: true},
56927         {header: "Market Cap.", width: 100, sortable: true},
56928         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56929         {header: "Employees", width: 100, sortable: true, resizable: false}
56930  ]);
56931  </code></pre>
56932  * <p>
56933  
56934  * The config options listed for this class are options which may appear in each
56935  * individual column definition.
56936  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56937  * @constructor
56938  * @param {Object} config An Array of column config objects. See this class's
56939  * config objects for details.
56940 */
56941 Roo.grid.ColumnModel = function(config){
56942         /**
56943      * The config passed into the constructor
56944      */
56945     this.config = config;
56946     this.lookup = {};
56947
56948     // if no id, create one
56949     // if the column does not have a dataIndex mapping,
56950     // map it to the order it is in the config
56951     for(var i = 0, len = config.length; i < len; i++){
56952         var c = config[i];
56953         if(typeof c.dataIndex == "undefined"){
56954             c.dataIndex = i;
56955         }
56956         if(typeof c.renderer == "string"){
56957             c.renderer = Roo.util.Format[c.renderer];
56958         }
56959         if(typeof c.id == "undefined"){
56960             c.id = Roo.id();
56961         }
56962         if(c.editor && c.editor.xtype){
56963             c.editor  = Roo.factory(c.editor, Roo.grid);
56964         }
56965         if(c.editor && c.editor.isFormField){
56966             c.editor = new Roo.grid.GridEditor(c.editor);
56967         }
56968         this.lookup[c.id] = c;
56969     }
56970
56971     /**
56972      * The width of columns which have no width specified (defaults to 100)
56973      * @type Number
56974      */
56975     this.defaultWidth = 100;
56976
56977     /**
56978      * Default sortable of columns which have no sortable specified (defaults to false)
56979      * @type Boolean
56980      */
56981     this.defaultSortable = false;
56982
56983     this.addEvents({
56984         /**
56985              * @event widthchange
56986              * Fires when the width of a column changes.
56987              * @param {ColumnModel} this
56988              * @param {Number} columnIndex The column index
56989              * @param {Number} newWidth The new width
56990              */
56991             "widthchange": true,
56992         /**
56993              * @event headerchange
56994              * Fires when the text of a header changes.
56995              * @param {ColumnModel} this
56996              * @param {Number} columnIndex The column index
56997              * @param {Number} newText The new header text
56998              */
56999             "headerchange": true,
57000         /**
57001              * @event hiddenchange
57002              * Fires when a column is hidden or "unhidden".
57003              * @param {ColumnModel} this
57004              * @param {Number} columnIndex The column index
57005              * @param {Boolean} hidden true if hidden, false otherwise
57006              */
57007             "hiddenchange": true,
57008             /**
57009          * @event columnmoved
57010          * Fires when a column is moved.
57011          * @param {ColumnModel} this
57012          * @param {Number} oldIndex
57013          * @param {Number} newIndex
57014          */
57015         "columnmoved" : true,
57016         /**
57017          * @event columlockchange
57018          * Fires when a column's locked state is changed
57019          * @param {ColumnModel} this
57020          * @param {Number} colIndex
57021          * @param {Boolean} locked true if locked
57022          */
57023         "columnlockchange" : true
57024     });
57025     Roo.grid.ColumnModel.superclass.constructor.call(this);
57026 };
57027 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57028     /**
57029      * @cfg {String} header The header text to display in the Grid view.
57030      */
57031     /**
57032      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57033      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57034      * specified, the column's index is used as an index into the Record's data Array.
57035      */
57036     /**
57037      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57038      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57039      */
57040     /**
57041      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57042      * Defaults to the value of the {@link #defaultSortable} property.
57043      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57044      */
57045     /**
57046      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57047      */
57048     /**
57049      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57050      */
57051     /**
57052      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57053      */
57054     /**
57055      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57056      */
57057     /**
57058      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57059      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57060      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57061      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57062      */
57063        /**
57064      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57065      */
57066     /**
57067      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57068      */
57069     /**
57070      * @cfg {String} cursor (Optional)
57071      */
57072     /**
57073      * @cfg {String} tooltip (Optional)
57074      */
57075     /**
57076      * @cfg {Number} xs (Optional)
57077      */
57078     /**
57079      * @cfg {Number} sm (Optional)
57080      */
57081     /**
57082      * @cfg {Number} md (Optional)
57083      */
57084     /**
57085      * @cfg {Number} lg (Optional)
57086      */
57087     /**
57088      * Returns the id of the column at the specified index.
57089      * @param {Number} index The column index
57090      * @return {String} the id
57091      */
57092     getColumnId : function(index){
57093         return this.config[index].id;
57094     },
57095
57096     /**
57097      * Returns the column for a specified id.
57098      * @param {String} id The column id
57099      * @return {Object} the column
57100      */
57101     getColumnById : function(id){
57102         return this.lookup[id];
57103     },
57104
57105     
57106     /**
57107      * Returns the column for a specified dataIndex.
57108      * @param {String} dataIndex The column dataIndex
57109      * @return {Object|Boolean} the column or false if not found
57110      */
57111     getColumnByDataIndex: function(dataIndex){
57112         var index = this.findColumnIndex(dataIndex);
57113         return index > -1 ? this.config[index] : false;
57114     },
57115     
57116     /**
57117      * Returns the index for a specified column id.
57118      * @param {String} id The column id
57119      * @return {Number} the index, or -1 if not found
57120      */
57121     getIndexById : function(id){
57122         for(var i = 0, len = this.config.length; i < len; i++){
57123             if(this.config[i].id == id){
57124                 return i;
57125             }
57126         }
57127         return -1;
57128     },
57129     
57130     /**
57131      * Returns the index for a specified column dataIndex.
57132      * @param {String} dataIndex The column dataIndex
57133      * @return {Number} the index, or -1 if not found
57134      */
57135     
57136     findColumnIndex : function(dataIndex){
57137         for(var i = 0, len = this.config.length; i < len; i++){
57138             if(this.config[i].dataIndex == dataIndex){
57139                 return i;
57140             }
57141         }
57142         return -1;
57143     },
57144     
57145     
57146     moveColumn : function(oldIndex, newIndex){
57147         var c = this.config[oldIndex];
57148         this.config.splice(oldIndex, 1);
57149         this.config.splice(newIndex, 0, c);
57150         this.dataMap = null;
57151         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57152     },
57153
57154     isLocked : function(colIndex){
57155         return this.config[colIndex].locked === true;
57156     },
57157
57158     setLocked : function(colIndex, value, suppressEvent){
57159         if(this.isLocked(colIndex) == value){
57160             return;
57161         }
57162         this.config[colIndex].locked = value;
57163         if(!suppressEvent){
57164             this.fireEvent("columnlockchange", this, colIndex, value);
57165         }
57166     },
57167
57168     getTotalLockedWidth : function(){
57169         var totalWidth = 0;
57170         for(var i = 0; i < this.config.length; i++){
57171             if(this.isLocked(i) && !this.isHidden(i)){
57172                 this.totalWidth += this.getColumnWidth(i);
57173             }
57174         }
57175         return totalWidth;
57176     },
57177
57178     getLockedCount : function(){
57179         for(var i = 0, len = this.config.length; i < len; i++){
57180             if(!this.isLocked(i)){
57181                 return i;
57182             }
57183         }
57184         
57185         return this.config.length;
57186     },
57187
57188     /**
57189      * Returns the number of columns.
57190      * @return {Number}
57191      */
57192     getColumnCount : function(visibleOnly){
57193         if(visibleOnly === true){
57194             var c = 0;
57195             for(var i = 0, len = this.config.length; i < len; i++){
57196                 if(!this.isHidden(i)){
57197                     c++;
57198                 }
57199             }
57200             return c;
57201         }
57202         return this.config.length;
57203     },
57204
57205     /**
57206      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57207      * @param {Function} fn
57208      * @param {Object} scope (optional)
57209      * @return {Array} result
57210      */
57211     getColumnsBy : function(fn, scope){
57212         var r = [];
57213         for(var i = 0, len = this.config.length; i < len; i++){
57214             var c = this.config[i];
57215             if(fn.call(scope||this, c, i) === true){
57216                 r[r.length] = c;
57217             }
57218         }
57219         return r;
57220     },
57221
57222     /**
57223      * Returns true if the specified column is sortable.
57224      * @param {Number} col The column index
57225      * @return {Boolean}
57226      */
57227     isSortable : function(col){
57228         if(typeof this.config[col].sortable == "undefined"){
57229             return this.defaultSortable;
57230         }
57231         return this.config[col].sortable;
57232     },
57233
57234     /**
57235      * Returns the rendering (formatting) function defined for the column.
57236      * @param {Number} col The column index.
57237      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57238      */
57239     getRenderer : function(col){
57240         if(!this.config[col].renderer){
57241             return Roo.grid.ColumnModel.defaultRenderer;
57242         }
57243         return this.config[col].renderer;
57244     },
57245
57246     /**
57247      * Sets the rendering (formatting) function for a column.
57248      * @param {Number} col The column index
57249      * @param {Function} fn The function to use to process the cell's raw data
57250      * to return HTML markup for the grid view. The render function is called with
57251      * the following parameters:<ul>
57252      * <li>Data value.</li>
57253      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57254      * <li>css A CSS style string to apply to the table cell.</li>
57255      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57256      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57257      * <li>Row index</li>
57258      * <li>Column index</li>
57259      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57260      */
57261     setRenderer : function(col, fn){
57262         this.config[col].renderer = fn;
57263     },
57264
57265     /**
57266      * Returns the width for the specified column.
57267      * @param {Number} col The column index
57268      * @return {Number}
57269      */
57270     getColumnWidth : function(col){
57271         return this.config[col].width * 1 || this.defaultWidth;
57272     },
57273
57274     /**
57275      * Sets the width for a column.
57276      * @param {Number} col The column index
57277      * @param {Number} width The new width
57278      */
57279     setColumnWidth : function(col, width, suppressEvent){
57280         this.config[col].width = width;
57281         this.totalWidth = null;
57282         if(!suppressEvent){
57283              this.fireEvent("widthchange", this, col, width);
57284         }
57285     },
57286
57287     /**
57288      * Returns the total width of all columns.
57289      * @param {Boolean} includeHidden True to include hidden column widths
57290      * @return {Number}
57291      */
57292     getTotalWidth : function(includeHidden){
57293         if(!this.totalWidth){
57294             this.totalWidth = 0;
57295             for(var i = 0, len = this.config.length; i < len; i++){
57296                 if(includeHidden || !this.isHidden(i)){
57297                     this.totalWidth += this.getColumnWidth(i);
57298                 }
57299             }
57300         }
57301         return this.totalWidth;
57302     },
57303
57304     /**
57305      * Returns the header for the specified column.
57306      * @param {Number} col The column index
57307      * @return {String}
57308      */
57309     getColumnHeader : function(col){
57310         return this.config[col].header;
57311     },
57312
57313     /**
57314      * Sets the header for a column.
57315      * @param {Number} col The column index
57316      * @param {String} header The new header
57317      */
57318     setColumnHeader : function(col, header){
57319         this.config[col].header = header;
57320         this.fireEvent("headerchange", this, col, header);
57321     },
57322
57323     /**
57324      * Returns the tooltip for the specified column.
57325      * @param {Number} col The column index
57326      * @return {String}
57327      */
57328     getColumnTooltip : function(col){
57329             return this.config[col].tooltip;
57330     },
57331     /**
57332      * Sets the tooltip for a column.
57333      * @param {Number} col The column index
57334      * @param {String} tooltip The new tooltip
57335      */
57336     setColumnTooltip : function(col, tooltip){
57337             this.config[col].tooltip = tooltip;
57338     },
57339
57340     /**
57341      * Returns the dataIndex for the specified column.
57342      * @param {Number} col The column index
57343      * @return {Number}
57344      */
57345     getDataIndex : function(col){
57346         return this.config[col].dataIndex;
57347     },
57348
57349     /**
57350      * Sets the dataIndex for a column.
57351      * @param {Number} col The column index
57352      * @param {Number} dataIndex The new dataIndex
57353      */
57354     setDataIndex : function(col, dataIndex){
57355         this.config[col].dataIndex = dataIndex;
57356     },
57357
57358     
57359     
57360     /**
57361      * Returns true if the cell is editable.
57362      * @param {Number} colIndex The column index
57363      * @param {Number} rowIndex The row index - this is nto actually used..?
57364      * @return {Boolean}
57365      */
57366     isCellEditable : function(colIndex, rowIndex){
57367         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57368     },
57369
57370     /**
57371      * Returns the editor defined for the cell/column.
57372      * return false or null to disable editing.
57373      * @param {Number} colIndex The column index
57374      * @param {Number} rowIndex The row index
57375      * @return {Object}
57376      */
57377     getCellEditor : function(colIndex, rowIndex){
57378         return this.config[colIndex].editor;
57379     },
57380
57381     /**
57382      * Sets if a column is editable.
57383      * @param {Number} col The column index
57384      * @param {Boolean} editable True if the column is editable
57385      */
57386     setEditable : function(col, editable){
57387         this.config[col].editable = editable;
57388     },
57389
57390
57391     /**
57392      * Returns true if the column is hidden.
57393      * @param {Number} colIndex The column index
57394      * @return {Boolean}
57395      */
57396     isHidden : function(colIndex){
57397         return this.config[colIndex].hidden;
57398     },
57399
57400
57401     /**
57402      * Returns true if the column width cannot be changed
57403      */
57404     isFixed : function(colIndex){
57405         return this.config[colIndex].fixed;
57406     },
57407
57408     /**
57409      * Returns true if the column can be resized
57410      * @return {Boolean}
57411      */
57412     isResizable : function(colIndex){
57413         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57414     },
57415     /**
57416      * Sets if a column is hidden.
57417      * @param {Number} colIndex The column index
57418      * @param {Boolean} hidden True if the column is hidden
57419      */
57420     setHidden : function(colIndex, hidden){
57421         this.config[colIndex].hidden = hidden;
57422         this.totalWidth = null;
57423         this.fireEvent("hiddenchange", this, colIndex, hidden);
57424     },
57425
57426     /**
57427      * Sets the editor for a column.
57428      * @param {Number} col The column index
57429      * @param {Object} editor The editor object
57430      */
57431     setEditor : function(col, editor){
57432         this.config[col].editor = editor;
57433     }
57434 });
57435
57436 Roo.grid.ColumnModel.defaultRenderer = function(value)
57437 {
57438     if(typeof value == "object") {
57439         return value;
57440     }
57441         if(typeof value == "string" && value.length < 1){
57442             return "&#160;";
57443         }
57444     
57445         return String.format("{0}", value);
57446 };
57447
57448 // Alias for backwards compatibility
57449 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57450 /*
57451  * Based on:
57452  * Ext JS Library 1.1.1
57453  * Copyright(c) 2006-2007, Ext JS, LLC.
57454  *
57455  * Originally Released Under LGPL - original licence link has changed is not relivant.
57456  *
57457  * Fork - LGPL
57458  * <script type="text/javascript">
57459  */
57460
57461 /**
57462  * @class Roo.grid.AbstractSelectionModel
57463  * @extends Roo.util.Observable
57464  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57465  * implemented by descendant classes.  This class should not be directly instantiated.
57466  * @constructor
57467  */
57468 Roo.grid.AbstractSelectionModel = function(){
57469     this.locked = false;
57470     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57471 };
57472
57473 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57474     /** @ignore Called by the grid automatically. Do not call directly. */
57475     init : function(grid){
57476         this.grid = grid;
57477         this.initEvents();
57478     },
57479
57480     /**
57481      * Locks the selections.
57482      */
57483     lock : function(){
57484         this.locked = true;
57485     },
57486
57487     /**
57488      * Unlocks the selections.
57489      */
57490     unlock : function(){
57491         this.locked = false;
57492     },
57493
57494     /**
57495      * Returns true if the selections are locked.
57496      * @return {Boolean}
57497      */
57498     isLocked : function(){
57499         return this.locked;
57500     }
57501 });/*
57502  * Based on:
57503  * Ext JS Library 1.1.1
57504  * Copyright(c) 2006-2007, Ext JS, LLC.
57505  *
57506  * Originally Released Under LGPL - original licence link has changed is not relivant.
57507  *
57508  * Fork - LGPL
57509  * <script type="text/javascript">
57510  */
57511 /**
57512  * @extends Roo.grid.AbstractSelectionModel
57513  * @class Roo.grid.RowSelectionModel
57514  * The default SelectionModel used by {@link Roo.grid.Grid}.
57515  * It supports multiple selections and keyboard selection/navigation. 
57516  * @constructor
57517  * @param {Object} config
57518  */
57519 Roo.grid.RowSelectionModel = function(config){
57520     Roo.apply(this, config);
57521     this.selections = new Roo.util.MixedCollection(false, function(o){
57522         return o.id;
57523     });
57524
57525     this.last = false;
57526     this.lastActive = false;
57527
57528     this.addEvents({
57529         /**
57530              * @event selectionchange
57531              * Fires when the selection changes
57532              * @param {SelectionModel} this
57533              */
57534             "selectionchange" : true,
57535         /**
57536              * @event afterselectionchange
57537              * Fires after the selection changes (eg. by key press or clicking)
57538              * @param {SelectionModel} this
57539              */
57540             "afterselectionchange" : true,
57541         /**
57542              * @event beforerowselect
57543              * Fires when a row is selected being selected, return false to cancel.
57544              * @param {SelectionModel} this
57545              * @param {Number} rowIndex The selected index
57546              * @param {Boolean} keepExisting False if other selections will be cleared
57547              */
57548             "beforerowselect" : true,
57549         /**
57550              * @event rowselect
57551              * Fires when a row is selected.
57552              * @param {SelectionModel} this
57553              * @param {Number} rowIndex The selected index
57554              * @param {Roo.data.Record} r The record
57555              */
57556             "rowselect" : true,
57557         /**
57558              * @event rowdeselect
57559              * Fires when a row is deselected.
57560              * @param {SelectionModel} this
57561              * @param {Number} rowIndex The selected index
57562              */
57563         "rowdeselect" : true
57564     });
57565     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57566     this.locked = false;
57567 };
57568
57569 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57570     /**
57571      * @cfg {Boolean} singleSelect
57572      * True to allow selection of only one row at a time (defaults to false)
57573      */
57574     singleSelect : false,
57575
57576     // private
57577     initEvents : function(){
57578
57579         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57580             this.grid.on("mousedown", this.handleMouseDown, this);
57581         }else{ // allow click to work like normal
57582             this.grid.on("rowclick", this.handleDragableRowClick, this);
57583         }
57584
57585         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57586             "up" : function(e){
57587                 if(!e.shiftKey){
57588                     this.selectPrevious(e.shiftKey);
57589                 }else if(this.last !== false && this.lastActive !== false){
57590                     var last = this.last;
57591                     this.selectRange(this.last,  this.lastActive-1);
57592                     this.grid.getView().focusRow(this.lastActive);
57593                     if(last !== false){
57594                         this.last = last;
57595                     }
57596                 }else{
57597                     this.selectFirstRow();
57598                 }
57599                 this.fireEvent("afterselectionchange", this);
57600             },
57601             "down" : function(e){
57602                 if(!e.shiftKey){
57603                     this.selectNext(e.shiftKey);
57604                 }else if(this.last !== false && this.lastActive !== false){
57605                     var last = this.last;
57606                     this.selectRange(this.last,  this.lastActive+1);
57607                     this.grid.getView().focusRow(this.lastActive);
57608                     if(last !== false){
57609                         this.last = last;
57610                     }
57611                 }else{
57612                     this.selectFirstRow();
57613                 }
57614                 this.fireEvent("afterselectionchange", this);
57615             },
57616             scope: this
57617         });
57618
57619         var view = this.grid.view;
57620         view.on("refresh", this.onRefresh, this);
57621         view.on("rowupdated", this.onRowUpdated, this);
57622         view.on("rowremoved", this.onRemove, this);
57623     },
57624
57625     // private
57626     onRefresh : function(){
57627         var ds = this.grid.dataSource, i, v = this.grid.view;
57628         var s = this.selections;
57629         s.each(function(r){
57630             if((i = ds.indexOfId(r.id)) != -1){
57631                 v.onRowSelect(i);
57632                 s.add(ds.getAt(i)); // updating the selection relate data
57633             }else{
57634                 s.remove(r);
57635             }
57636         });
57637     },
57638
57639     // private
57640     onRemove : function(v, index, r){
57641         this.selections.remove(r);
57642     },
57643
57644     // private
57645     onRowUpdated : function(v, index, r){
57646         if(this.isSelected(r)){
57647             v.onRowSelect(index);
57648         }
57649     },
57650
57651     /**
57652      * Select records.
57653      * @param {Array} records The records to select
57654      * @param {Boolean} keepExisting (optional) True to keep existing selections
57655      */
57656     selectRecords : function(records, keepExisting){
57657         if(!keepExisting){
57658             this.clearSelections();
57659         }
57660         var ds = this.grid.dataSource;
57661         for(var i = 0, len = records.length; i < len; i++){
57662             this.selectRow(ds.indexOf(records[i]), true);
57663         }
57664     },
57665
57666     /**
57667      * Gets the number of selected rows.
57668      * @return {Number}
57669      */
57670     getCount : function(){
57671         return this.selections.length;
57672     },
57673
57674     /**
57675      * Selects the first row in the grid.
57676      */
57677     selectFirstRow : function(){
57678         this.selectRow(0);
57679     },
57680
57681     /**
57682      * Select the last row.
57683      * @param {Boolean} keepExisting (optional) True to keep existing selections
57684      */
57685     selectLastRow : function(keepExisting){
57686         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57687     },
57688
57689     /**
57690      * Selects the row immediately following the last selected row.
57691      * @param {Boolean} keepExisting (optional) True to keep existing selections
57692      */
57693     selectNext : function(keepExisting){
57694         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57695             this.selectRow(this.last+1, keepExisting);
57696             this.grid.getView().focusRow(this.last);
57697         }
57698     },
57699
57700     /**
57701      * Selects the row that precedes the last selected row.
57702      * @param {Boolean} keepExisting (optional) True to keep existing selections
57703      */
57704     selectPrevious : function(keepExisting){
57705         if(this.last){
57706             this.selectRow(this.last-1, keepExisting);
57707             this.grid.getView().focusRow(this.last);
57708         }
57709     },
57710
57711     /**
57712      * Returns the selected records
57713      * @return {Array} Array of selected records
57714      */
57715     getSelections : function(){
57716         return [].concat(this.selections.items);
57717     },
57718
57719     /**
57720      * Returns the first selected record.
57721      * @return {Record}
57722      */
57723     getSelected : function(){
57724         return this.selections.itemAt(0);
57725     },
57726
57727
57728     /**
57729      * Clears all selections.
57730      */
57731     clearSelections : function(fast){
57732         if(this.locked) {
57733             return;
57734         }
57735         if(fast !== true){
57736             var ds = this.grid.dataSource;
57737             var s = this.selections;
57738             s.each(function(r){
57739                 this.deselectRow(ds.indexOfId(r.id));
57740             }, this);
57741             s.clear();
57742         }else{
57743             this.selections.clear();
57744         }
57745         this.last = false;
57746     },
57747
57748
57749     /**
57750      * Selects all rows.
57751      */
57752     selectAll : function(){
57753         if(this.locked) {
57754             return;
57755         }
57756         this.selections.clear();
57757         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57758             this.selectRow(i, true);
57759         }
57760     },
57761
57762     /**
57763      * Returns True if there is a selection.
57764      * @return {Boolean}
57765      */
57766     hasSelection : function(){
57767         return this.selections.length > 0;
57768     },
57769
57770     /**
57771      * Returns True if the specified row is selected.
57772      * @param {Number/Record} record The record or index of the record to check
57773      * @return {Boolean}
57774      */
57775     isSelected : function(index){
57776         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57777         return (r && this.selections.key(r.id) ? true : false);
57778     },
57779
57780     /**
57781      * Returns True if the specified record id is selected.
57782      * @param {String} id The id of record to check
57783      * @return {Boolean}
57784      */
57785     isIdSelected : function(id){
57786         return (this.selections.key(id) ? true : false);
57787     },
57788
57789     // private
57790     handleMouseDown : function(e, t){
57791         var view = this.grid.getView(), rowIndex;
57792         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57793             return;
57794         };
57795         if(e.shiftKey && this.last !== false){
57796             var last = this.last;
57797             this.selectRange(last, rowIndex, e.ctrlKey);
57798             this.last = last; // reset the last
57799             view.focusRow(rowIndex);
57800         }else{
57801             var isSelected = this.isSelected(rowIndex);
57802             if(e.button !== 0 && isSelected){
57803                 view.focusRow(rowIndex);
57804             }else if(e.ctrlKey && isSelected){
57805                 this.deselectRow(rowIndex);
57806             }else if(!isSelected){
57807                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57808                 view.focusRow(rowIndex);
57809             }
57810         }
57811         this.fireEvent("afterselectionchange", this);
57812     },
57813     // private
57814     handleDragableRowClick :  function(grid, rowIndex, e) 
57815     {
57816         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57817             this.selectRow(rowIndex, false);
57818             grid.view.focusRow(rowIndex);
57819              this.fireEvent("afterselectionchange", this);
57820         }
57821     },
57822     
57823     /**
57824      * Selects multiple rows.
57825      * @param {Array} rows Array of the indexes of the row to select
57826      * @param {Boolean} keepExisting (optional) True to keep existing selections
57827      */
57828     selectRows : function(rows, keepExisting){
57829         if(!keepExisting){
57830             this.clearSelections();
57831         }
57832         for(var i = 0, len = rows.length; i < len; i++){
57833             this.selectRow(rows[i], true);
57834         }
57835     },
57836
57837     /**
57838      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57839      * @param {Number} startRow The index of the first row in the range
57840      * @param {Number} endRow The index of the last row in the range
57841      * @param {Boolean} keepExisting (optional) True to retain existing selections
57842      */
57843     selectRange : function(startRow, endRow, keepExisting){
57844         if(this.locked) {
57845             return;
57846         }
57847         if(!keepExisting){
57848             this.clearSelections();
57849         }
57850         if(startRow <= endRow){
57851             for(var i = startRow; i <= endRow; i++){
57852                 this.selectRow(i, true);
57853             }
57854         }else{
57855             for(var i = startRow; i >= endRow; i--){
57856                 this.selectRow(i, true);
57857             }
57858         }
57859     },
57860
57861     /**
57862      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57863      * @param {Number} startRow The index of the first row in the range
57864      * @param {Number} endRow The index of the last row in the range
57865      */
57866     deselectRange : function(startRow, endRow, preventViewNotify){
57867         if(this.locked) {
57868             return;
57869         }
57870         for(var i = startRow; i <= endRow; i++){
57871             this.deselectRow(i, preventViewNotify);
57872         }
57873     },
57874
57875     /**
57876      * Selects a row.
57877      * @param {Number} row The index of the row to select
57878      * @param {Boolean} keepExisting (optional) True to keep existing selections
57879      */
57880     selectRow : function(index, keepExisting, preventViewNotify){
57881         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57882             return;
57883         }
57884         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57885             if(!keepExisting || this.singleSelect){
57886                 this.clearSelections();
57887             }
57888             var r = this.grid.dataSource.getAt(index);
57889             this.selections.add(r);
57890             this.last = this.lastActive = index;
57891             if(!preventViewNotify){
57892                 this.grid.getView().onRowSelect(index);
57893             }
57894             this.fireEvent("rowselect", this, index, r);
57895             this.fireEvent("selectionchange", this);
57896         }
57897     },
57898
57899     /**
57900      * Deselects a row.
57901      * @param {Number} row The index of the row to deselect
57902      */
57903     deselectRow : function(index, preventViewNotify){
57904         if(this.locked) {
57905             return;
57906         }
57907         if(this.last == index){
57908             this.last = false;
57909         }
57910         if(this.lastActive == index){
57911             this.lastActive = false;
57912         }
57913         var r = this.grid.dataSource.getAt(index);
57914         this.selections.remove(r);
57915         if(!preventViewNotify){
57916             this.grid.getView().onRowDeselect(index);
57917         }
57918         this.fireEvent("rowdeselect", this, index);
57919         this.fireEvent("selectionchange", this);
57920     },
57921
57922     // private
57923     restoreLast : function(){
57924         if(this._last){
57925             this.last = this._last;
57926         }
57927     },
57928
57929     // private
57930     acceptsNav : function(row, col, cm){
57931         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57932     },
57933
57934     // private
57935     onEditorKey : function(field, e){
57936         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57937         if(k == e.TAB){
57938             e.stopEvent();
57939             ed.completeEdit();
57940             if(e.shiftKey){
57941                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57942             }else{
57943                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57944             }
57945         }else if(k == e.ENTER && !e.ctrlKey){
57946             e.stopEvent();
57947             ed.completeEdit();
57948             if(e.shiftKey){
57949                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57950             }else{
57951                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57952             }
57953         }else if(k == e.ESC){
57954             ed.cancelEdit();
57955         }
57956         if(newCell){
57957             g.startEditing(newCell[0], newCell[1]);
57958         }
57959     }
57960 });/*
57961  * Based on:
57962  * Ext JS Library 1.1.1
57963  * Copyright(c) 2006-2007, Ext JS, LLC.
57964  *
57965  * Originally Released Under LGPL - original licence link has changed is not relivant.
57966  *
57967  * Fork - LGPL
57968  * <script type="text/javascript">
57969  */
57970 /**
57971  * @class Roo.grid.CellSelectionModel
57972  * @extends Roo.grid.AbstractSelectionModel
57973  * This class provides the basic implementation for cell selection in a grid.
57974  * @constructor
57975  * @param {Object} config The object containing the configuration of this model.
57976  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
57977  */
57978 Roo.grid.CellSelectionModel = function(config){
57979     Roo.apply(this, config);
57980
57981     this.selection = null;
57982
57983     this.addEvents({
57984         /**
57985              * @event beforerowselect
57986              * Fires before a cell is selected.
57987              * @param {SelectionModel} this
57988              * @param {Number} rowIndex The selected row index
57989              * @param {Number} colIndex The selected cell index
57990              */
57991             "beforecellselect" : true,
57992         /**
57993              * @event cellselect
57994              * Fires when a cell is selected.
57995              * @param {SelectionModel} this
57996              * @param {Number} rowIndex The selected row index
57997              * @param {Number} colIndex The selected cell index
57998              */
57999             "cellselect" : true,
58000         /**
58001              * @event selectionchange
58002              * Fires when the active selection changes.
58003              * @param {SelectionModel} this
58004              * @param {Object} selection null for no selection or an object (o) with two properties
58005                 <ul>
58006                 <li>o.record: the record object for the row the selection is in</li>
58007                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58008                 </ul>
58009              */
58010             "selectionchange" : true,
58011         /**
58012              * @event tabend
58013              * Fires when the tab (or enter) was pressed on the last editable cell
58014              * You can use this to trigger add new row.
58015              * @param {SelectionModel} this
58016              */
58017             "tabend" : true,
58018          /**
58019              * @event beforeeditnext
58020              * Fires before the next editable sell is made active
58021              * You can use this to skip to another cell or fire the tabend
58022              *    if you set cell to false
58023              * @param {Object} eventdata object : { cell : [ row, col ] } 
58024              */
58025             "beforeeditnext" : true
58026     });
58027     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58028 };
58029
58030 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58031     
58032     enter_is_tab: false,
58033
58034     /** @ignore */
58035     initEvents : function(){
58036         this.grid.on("mousedown", this.handleMouseDown, this);
58037         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58038         var view = this.grid.view;
58039         view.on("refresh", this.onViewChange, this);
58040         view.on("rowupdated", this.onRowUpdated, this);
58041         view.on("beforerowremoved", this.clearSelections, this);
58042         view.on("beforerowsinserted", this.clearSelections, this);
58043         if(this.grid.isEditor){
58044             this.grid.on("beforeedit", this.beforeEdit,  this);
58045         }
58046     },
58047
58048         //private
58049     beforeEdit : function(e){
58050         this.select(e.row, e.column, false, true, e.record);
58051     },
58052
58053         //private
58054     onRowUpdated : function(v, index, r){
58055         if(this.selection && this.selection.record == r){
58056             v.onCellSelect(index, this.selection.cell[1]);
58057         }
58058     },
58059
58060         //private
58061     onViewChange : function(){
58062         this.clearSelections(true);
58063     },
58064
58065         /**
58066          * Returns the currently selected cell,.
58067          * @return {Array} The selected cell (row, column) or null if none selected.
58068          */
58069     getSelectedCell : function(){
58070         return this.selection ? this.selection.cell : null;
58071     },
58072
58073     /**
58074      * Clears all selections.
58075      * @param {Boolean} true to prevent the gridview from being notified about the change.
58076      */
58077     clearSelections : function(preventNotify){
58078         var s = this.selection;
58079         if(s){
58080             if(preventNotify !== true){
58081                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58082             }
58083             this.selection = null;
58084             this.fireEvent("selectionchange", this, null);
58085         }
58086     },
58087
58088     /**
58089      * Returns true if there is a selection.
58090      * @return {Boolean}
58091      */
58092     hasSelection : function(){
58093         return this.selection ? true : false;
58094     },
58095
58096     /** @ignore */
58097     handleMouseDown : function(e, t){
58098         var v = this.grid.getView();
58099         if(this.isLocked()){
58100             return;
58101         };
58102         var row = v.findRowIndex(t);
58103         var cell = v.findCellIndex(t);
58104         if(row !== false && cell !== false){
58105             this.select(row, cell);
58106         }
58107     },
58108
58109     /**
58110      * Selects a cell.
58111      * @param {Number} rowIndex
58112      * @param {Number} collIndex
58113      */
58114     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58115         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58116             this.clearSelections();
58117             r = r || this.grid.dataSource.getAt(rowIndex);
58118             this.selection = {
58119                 record : r,
58120                 cell : [rowIndex, colIndex]
58121             };
58122             if(!preventViewNotify){
58123                 var v = this.grid.getView();
58124                 v.onCellSelect(rowIndex, colIndex);
58125                 if(preventFocus !== true){
58126                     v.focusCell(rowIndex, colIndex);
58127                 }
58128             }
58129             this.fireEvent("cellselect", this, rowIndex, colIndex);
58130             this.fireEvent("selectionchange", this, this.selection);
58131         }
58132     },
58133
58134         //private
58135     isSelectable : function(rowIndex, colIndex, cm){
58136         return !cm.isHidden(colIndex);
58137     },
58138
58139     /** @ignore */
58140     handleKeyDown : function(e){
58141         //Roo.log('Cell Sel Model handleKeyDown');
58142         if(!e.isNavKeyPress()){
58143             return;
58144         }
58145         var g = this.grid, s = this.selection;
58146         if(!s){
58147             e.stopEvent();
58148             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58149             if(cell){
58150                 this.select(cell[0], cell[1]);
58151             }
58152             return;
58153         }
58154         var sm = this;
58155         var walk = function(row, col, step){
58156             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58157         };
58158         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58159         var newCell;
58160
58161       
58162
58163         switch(k){
58164             case e.TAB:
58165                 // handled by onEditorKey
58166                 if (g.isEditor && g.editing) {
58167                     return;
58168                 }
58169                 if(e.shiftKey) {
58170                     newCell = walk(r, c-1, -1);
58171                 } else {
58172                     newCell = walk(r, c+1, 1);
58173                 }
58174                 break;
58175             
58176             case e.DOWN:
58177                newCell = walk(r+1, c, 1);
58178                 break;
58179             
58180             case e.UP:
58181                 newCell = walk(r-1, c, -1);
58182                 break;
58183             
58184             case e.RIGHT:
58185                 newCell = walk(r, c+1, 1);
58186                 break;
58187             
58188             case e.LEFT:
58189                 newCell = walk(r, c-1, -1);
58190                 break;
58191             
58192             case e.ENTER:
58193                 
58194                 if(g.isEditor && !g.editing){
58195                    g.startEditing(r, c);
58196                    e.stopEvent();
58197                    return;
58198                 }
58199                 
58200                 
58201              break;
58202         };
58203         if(newCell){
58204             this.select(newCell[0], newCell[1]);
58205             e.stopEvent();
58206             
58207         }
58208     },
58209
58210     acceptsNav : function(row, col, cm){
58211         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58212     },
58213     /**
58214      * Selects a cell.
58215      * @param {Number} field (not used) - as it's normally used as a listener
58216      * @param {Number} e - event - fake it by using
58217      *
58218      * var e = Roo.EventObjectImpl.prototype;
58219      * e.keyCode = e.TAB
58220      *
58221      * 
58222      */
58223     onEditorKey : function(field, e){
58224         
58225         var k = e.getKey(),
58226             newCell,
58227             g = this.grid,
58228             ed = g.activeEditor,
58229             forward = false;
58230         ///Roo.log('onEditorKey' + k);
58231         
58232         
58233         if (this.enter_is_tab && k == e.ENTER) {
58234             k = e.TAB;
58235         }
58236         
58237         if(k == e.TAB){
58238             if(e.shiftKey){
58239                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58240             }else{
58241                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58242                 forward = true;
58243             }
58244             
58245             e.stopEvent();
58246             
58247         } else if(k == e.ENTER &&  !e.ctrlKey){
58248             ed.completeEdit();
58249             e.stopEvent();
58250             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58251         
58252                 } else if(k == e.ESC){
58253             ed.cancelEdit();
58254         }
58255                 
58256         if (newCell) {
58257             var ecall = { cell : newCell, forward : forward };
58258             this.fireEvent('beforeeditnext', ecall );
58259             newCell = ecall.cell;
58260                         forward = ecall.forward;
58261         }
58262                 
58263         if(newCell){
58264             //Roo.log('next cell after edit');
58265             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58266         } else if (forward) {
58267             // tabbed past last
58268             this.fireEvent.defer(100, this, ['tabend',this]);
58269         }
58270     }
58271 });/*
58272  * Based on:
58273  * Ext JS Library 1.1.1
58274  * Copyright(c) 2006-2007, Ext JS, LLC.
58275  *
58276  * Originally Released Under LGPL - original licence link has changed is not relivant.
58277  *
58278  * Fork - LGPL
58279  * <script type="text/javascript">
58280  */
58281  
58282 /**
58283  * @class Roo.grid.EditorGrid
58284  * @extends Roo.grid.Grid
58285  * Class for creating and editable grid.
58286  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58287  * The container MUST have some type of size defined for the grid to fill. The container will be 
58288  * automatically set to position relative if it isn't already.
58289  * @param {Object} dataSource The data model to bind to
58290  * @param {Object} colModel The column model with info about this grid's columns
58291  */
58292 Roo.grid.EditorGrid = function(container, config){
58293     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58294     this.getGridEl().addClass("xedit-grid");
58295
58296     if(!this.selModel){
58297         this.selModel = new Roo.grid.CellSelectionModel();
58298     }
58299
58300     this.activeEditor = null;
58301
58302         this.addEvents({
58303             /**
58304              * @event beforeedit
58305              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58306              * <ul style="padding:5px;padding-left:16px;">
58307              * <li>grid - This grid</li>
58308              * <li>record - The record being edited</li>
58309              * <li>field - The field name being edited</li>
58310              * <li>value - The value for the field being edited.</li>
58311              * <li>row - The grid row index</li>
58312              * <li>column - The grid column index</li>
58313              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58314              * </ul>
58315              * @param {Object} e An edit event (see above for description)
58316              */
58317             "beforeedit" : true,
58318             /**
58319              * @event afteredit
58320              * Fires after a cell is edited. <br />
58321              * <ul style="padding:5px;padding-left:16px;">
58322              * <li>grid - This grid</li>
58323              * <li>record - The record being edited</li>
58324              * <li>field - The field name being edited</li>
58325              * <li>value - The value being set</li>
58326              * <li>originalValue - The original value for the field, before the edit.</li>
58327              * <li>row - The grid row index</li>
58328              * <li>column - The grid column index</li>
58329              * </ul>
58330              * @param {Object} e An edit event (see above for description)
58331              */
58332             "afteredit" : true,
58333             /**
58334              * @event validateedit
58335              * Fires after a cell is edited, but before the value is set in the record. 
58336          * You can use this to modify the value being set in the field, Return false
58337              * to cancel the change. The edit event object has the following properties <br />
58338              * <ul style="padding:5px;padding-left:16px;">
58339          * <li>editor - This editor</li>
58340              * <li>grid - This grid</li>
58341              * <li>record - The record being edited</li>
58342              * <li>field - The field name being edited</li>
58343              * <li>value - The value being set</li>
58344              * <li>originalValue - The original value for the field, before the edit.</li>
58345              * <li>row - The grid row index</li>
58346              * <li>column - The grid column index</li>
58347              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58348              * </ul>
58349              * @param {Object} e An edit event (see above for description)
58350              */
58351             "validateedit" : true
58352         });
58353     this.on("bodyscroll", this.stopEditing,  this);
58354     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58355 };
58356
58357 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58358     /**
58359      * @cfg {Number} clicksToEdit
58360      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58361      */
58362     clicksToEdit: 2,
58363
58364     // private
58365     isEditor : true,
58366     // private
58367     trackMouseOver: false, // causes very odd FF errors
58368
58369     onCellDblClick : function(g, row, col){
58370         this.startEditing(row, col);
58371     },
58372
58373     onEditComplete : function(ed, value, startValue){
58374         this.editing = false;
58375         this.activeEditor = null;
58376         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58377         var r = ed.record;
58378         var field = this.colModel.getDataIndex(ed.col);
58379         var e = {
58380             grid: this,
58381             record: r,
58382             field: field,
58383             originalValue: startValue,
58384             value: value,
58385             row: ed.row,
58386             column: ed.col,
58387             cancel:false,
58388             editor: ed
58389         };
58390         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58391         cell.show();
58392           
58393         if(String(value) !== String(startValue)){
58394             
58395             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58396                 r.set(field, e.value);
58397                 // if we are dealing with a combo box..
58398                 // then we also set the 'name' colum to be the displayField
58399                 if (ed.field.displayField && ed.field.name) {
58400                     r.set(ed.field.name, ed.field.el.dom.value);
58401                 }
58402                 
58403                 delete e.cancel; //?? why!!!
58404                 this.fireEvent("afteredit", e);
58405             }
58406         } else {
58407             this.fireEvent("afteredit", e); // always fire it!
58408         }
58409         this.view.focusCell(ed.row, ed.col);
58410     },
58411
58412     /**
58413      * Starts editing the specified for the specified row/column
58414      * @param {Number} rowIndex
58415      * @param {Number} colIndex
58416      */
58417     startEditing : function(row, col){
58418         this.stopEditing();
58419         if(this.colModel.isCellEditable(col, row)){
58420             this.view.ensureVisible(row, col, true);
58421           
58422             var r = this.dataSource.getAt(row);
58423             var field = this.colModel.getDataIndex(col);
58424             var cell = Roo.get(this.view.getCell(row,col));
58425             var e = {
58426                 grid: this,
58427                 record: r,
58428                 field: field,
58429                 value: r.data[field],
58430                 row: row,
58431                 column: col,
58432                 cancel:false 
58433             };
58434             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58435                 this.editing = true;
58436                 var ed = this.colModel.getCellEditor(col, row);
58437                 
58438                 if (!ed) {
58439                     return;
58440                 }
58441                 if(!ed.rendered){
58442                     ed.render(ed.parentEl || document.body);
58443                 }
58444                 ed.field.reset();
58445                
58446                 cell.hide();
58447                 
58448                 (function(){ // complex but required for focus issues in safari, ie and opera
58449                     ed.row = row;
58450                     ed.col = col;
58451                     ed.record = r;
58452                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58453                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58454                     this.activeEditor = ed;
58455                     var v = r.data[field];
58456                     ed.startEdit(this.view.getCell(row, col), v);
58457                     // combo's with 'displayField and name set
58458                     if (ed.field.displayField && ed.field.name) {
58459                         ed.field.el.dom.value = r.data[ed.field.name];
58460                     }
58461                     
58462                     
58463                 }).defer(50, this);
58464             }
58465         }
58466     },
58467         
58468     /**
58469      * Stops any active editing
58470      */
58471     stopEditing : function(){
58472         if(this.activeEditor){
58473             this.activeEditor.completeEdit();
58474         }
58475         this.activeEditor = null;
58476     },
58477         
58478          /**
58479      * Called to get grid's drag proxy text, by default returns this.ddText.
58480      * @return {String}
58481      */
58482     getDragDropText : function(){
58483         var count = this.selModel.getSelectedCell() ? 1 : 0;
58484         return String.format(this.ddText, count, count == 1 ? '' : 's');
58485     }
58486         
58487 });/*
58488  * Based on:
58489  * Ext JS Library 1.1.1
58490  * Copyright(c) 2006-2007, Ext JS, LLC.
58491  *
58492  * Originally Released Under LGPL - original licence link has changed is not relivant.
58493  *
58494  * Fork - LGPL
58495  * <script type="text/javascript">
58496  */
58497
58498 // private - not really -- you end up using it !
58499 // This is a support class used internally by the Grid components
58500
58501 /**
58502  * @class Roo.grid.GridEditor
58503  * @extends Roo.Editor
58504  * Class for creating and editable grid elements.
58505  * @param {Object} config any settings (must include field)
58506  */
58507 Roo.grid.GridEditor = function(field, config){
58508     if (!config && field.field) {
58509         config = field;
58510         field = Roo.factory(config.field, Roo.form);
58511     }
58512     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58513     field.monitorTab = false;
58514 };
58515
58516 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58517     
58518     /**
58519      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58520      */
58521     
58522     alignment: "tl-tl",
58523     autoSize: "width",
58524     hideEl : false,
58525     cls: "x-small-editor x-grid-editor",
58526     shim:false,
58527     shadow:"frame"
58528 });/*
58529  * Based on:
58530  * Ext JS Library 1.1.1
58531  * Copyright(c) 2006-2007, Ext JS, LLC.
58532  *
58533  * Originally Released Under LGPL - original licence link has changed is not relivant.
58534  *
58535  * Fork - LGPL
58536  * <script type="text/javascript">
58537  */
58538   
58539
58540   
58541 Roo.grid.PropertyRecord = Roo.data.Record.create([
58542     {name:'name',type:'string'},  'value'
58543 ]);
58544
58545
58546 Roo.grid.PropertyStore = function(grid, source){
58547     this.grid = grid;
58548     this.store = new Roo.data.Store({
58549         recordType : Roo.grid.PropertyRecord
58550     });
58551     this.store.on('update', this.onUpdate,  this);
58552     if(source){
58553         this.setSource(source);
58554     }
58555     Roo.grid.PropertyStore.superclass.constructor.call(this);
58556 };
58557
58558
58559
58560 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58561     setSource : function(o){
58562         this.source = o;
58563         this.store.removeAll();
58564         var data = [];
58565         for(var k in o){
58566             if(this.isEditableValue(o[k])){
58567                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58568             }
58569         }
58570         this.store.loadRecords({records: data}, {}, true);
58571     },
58572
58573     onUpdate : function(ds, record, type){
58574         if(type == Roo.data.Record.EDIT){
58575             var v = record.data['value'];
58576             var oldValue = record.modified['value'];
58577             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58578                 this.source[record.id] = v;
58579                 record.commit();
58580                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58581             }else{
58582                 record.reject();
58583             }
58584         }
58585     },
58586
58587     getProperty : function(row){
58588        return this.store.getAt(row);
58589     },
58590
58591     isEditableValue: function(val){
58592         if(val && val instanceof Date){
58593             return true;
58594         }else if(typeof val == 'object' || typeof val == 'function'){
58595             return false;
58596         }
58597         return true;
58598     },
58599
58600     setValue : function(prop, value){
58601         this.source[prop] = value;
58602         this.store.getById(prop).set('value', value);
58603     },
58604
58605     getSource : function(){
58606         return this.source;
58607     }
58608 });
58609
58610 Roo.grid.PropertyColumnModel = function(grid, store){
58611     this.grid = grid;
58612     var g = Roo.grid;
58613     g.PropertyColumnModel.superclass.constructor.call(this, [
58614         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58615         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58616     ]);
58617     this.store = store;
58618     this.bselect = Roo.DomHelper.append(document.body, {
58619         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58620             {tag: 'option', value: 'true', html: 'true'},
58621             {tag: 'option', value: 'false', html: 'false'}
58622         ]
58623     });
58624     Roo.id(this.bselect);
58625     var f = Roo.form;
58626     this.editors = {
58627         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58628         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58629         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58630         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58631         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58632     };
58633     this.renderCellDelegate = this.renderCell.createDelegate(this);
58634     this.renderPropDelegate = this.renderProp.createDelegate(this);
58635 };
58636
58637 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58638     
58639     
58640     nameText : 'Name',
58641     valueText : 'Value',
58642     
58643     dateFormat : 'm/j/Y',
58644     
58645     
58646     renderDate : function(dateVal){
58647         return dateVal.dateFormat(this.dateFormat);
58648     },
58649
58650     renderBool : function(bVal){
58651         return bVal ? 'true' : 'false';
58652     },
58653
58654     isCellEditable : function(colIndex, rowIndex){
58655         return colIndex == 1;
58656     },
58657
58658     getRenderer : function(col){
58659         return col == 1 ?
58660             this.renderCellDelegate : this.renderPropDelegate;
58661     },
58662
58663     renderProp : function(v){
58664         return this.getPropertyName(v);
58665     },
58666
58667     renderCell : function(val){
58668         var rv = val;
58669         if(val instanceof Date){
58670             rv = this.renderDate(val);
58671         }else if(typeof val == 'boolean'){
58672             rv = this.renderBool(val);
58673         }
58674         return Roo.util.Format.htmlEncode(rv);
58675     },
58676
58677     getPropertyName : function(name){
58678         var pn = this.grid.propertyNames;
58679         return pn && pn[name] ? pn[name] : name;
58680     },
58681
58682     getCellEditor : function(colIndex, rowIndex){
58683         var p = this.store.getProperty(rowIndex);
58684         var n = p.data['name'], val = p.data['value'];
58685         
58686         if(typeof(this.grid.customEditors[n]) == 'string'){
58687             return this.editors[this.grid.customEditors[n]];
58688         }
58689         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58690             return this.grid.customEditors[n];
58691         }
58692         if(val instanceof Date){
58693             return this.editors['date'];
58694         }else if(typeof val == 'number'){
58695             return this.editors['number'];
58696         }else if(typeof val == 'boolean'){
58697             return this.editors['boolean'];
58698         }else{
58699             return this.editors['string'];
58700         }
58701     }
58702 });
58703
58704 /**
58705  * @class Roo.grid.PropertyGrid
58706  * @extends Roo.grid.EditorGrid
58707  * This class represents the  interface of a component based property grid control.
58708  * <br><br>Usage:<pre><code>
58709  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58710       
58711  });
58712  // set any options
58713  grid.render();
58714  * </code></pre>
58715   
58716  * @constructor
58717  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58718  * The container MUST have some type of size defined for the grid to fill. The container will be
58719  * automatically set to position relative if it isn't already.
58720  * @param {Object} config A config object that sets properties on this grid.
58721  */
58722 Roo.grid.PropertyGrid = function(container, config){
58723     config = config || {};
58724     var store = new Roo.grid.PropertyStore(this);
58725     this.store = store;
58726     var cm = new Roo.grid.PropertyColumnModel(this, store);
58727     store.store.sort('name', 'ASC');
58728     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58729         ds: store.store,
58730         cm: cm,
58731         enableColLock:false,
58732         enableColumnMove:false,
58733         stripeRows:false,
58734         trackMouseOver: false,
58735         clicksToEdit:1
58736     }, config));
58737     this.getGridEl().addClass('x-props-grid');
58738     this.lastEditRow = null;
58739     this.on('columnresize', this.onColumnResize, this);
58740     this.addEvents({
58741          /**
58742              * @event beforepropertychange
58743              * Fires before a property changes (return false to stop?)
58744              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58745              * @param {String} id Record Id
58746              * @param {String} newval New Value
58747          * @param {String} oldval Old Value
58748              */
58749         "beforepropertychange": true,
58750         /**
58751              * @event propertychange
58752              * Fires after a property changes
58753              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58754              * @param {String} id Record Id
58755              * @param {String} newval New Value
58756          * @param {String} oldval Old Value
58757              */
58758         "propertychange": true
58759     });
58760     this.customEditors = this.customEditors || {};
58761 };
58762 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58763     
58764      /**
58765      * @cfg {Object} customEditors map of colnames=> custom editors.
58766      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58767      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58768      * false disables editing of the field.
58769          */
58770     
58771       /**
58772      * @cfg {Object} propertyNames map of property Names to their displayed value
58773          */
58774     
58775     render : function(){
58776         Roo.grid.PropertyGrid.superclass.render.call(this);
58777         this.autoSize.defer(100, this);
58778     },
58779
58780     autoSize : function(){
58781         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58782         if(this.view){
58783             this.view.fitColumns();
58784         }
58785     },
58786
58787     onColumnResize : function(){
58788         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58789         this.autoSize();
58790     },
58791     /**
58792      * Sets the data for the Grid
58793      * accepts a Key => Value object of all the elements avaiable.
58794      * @param {Object} data  to appear in grid.
58795      */
58796     setSource : function(source){
58797         this.store.setSource(source);
58798         //this.autoSize();
58799     },
58800     /**
58801      * Gets all the data from the grid.
58802      * @return {Object} data  data stored in grid
58803      */
58804     getSource : function(){
58805         return this.store.getSource();
58806     }
58807 });/*
58808   
58809  * Licence LGPL
58810  
58811  */
58812  
58813 /**
58814  * @class Roo.grid.Calendar
58815  * @extends Roo.util.Grid
58816  * This class extends the Grid to provide a calendar widget
58817  * <br><br>Usage:<pre><code>
58818  var grid = new Roo.grid.Calendar("my-container-id", {
58819      ds: myDataStore,
58820      cm: myColModel,
58821      selModel: mySelectionModel,
58822      autoSizeColumns: true,
58823      monitorWindowResize: false,
58824      trackMouseOver: true
58825      eventstore : real data store..
58826  });
58827  // set any options
58828  grid.render();
58829   
58830   * @constructor
58831  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58832  * The container MUST have some type of size defined for the grid to fill. The container will be
58833  * automatically set to position relative if it isn't already.
58834  * @param {Object} config A config object that sets properties on this grid.
58835  */
58836 Roo.grid.Calendar = function(container, config){
58837         // initialize the container
58838         this.container = Roo.get(container);
58839         this.container.update("");
58840         this.container.setStyle("overflow", "hidden");
58841     this.container.addClass('x-grid-container');
58842
58843     this.id = this.container.id;
58844
58845     Roo.apply(this, config);
58846     // check and correct shorthanded configs
58847     
58848     var rows = [];
58849     var d =1;
58850     for (var r = 0;r < 6;r++) {
58851         
58852         rows[r]=[];
58853         for (var c =0;c < 7;c++) {
58854             rows[r][c]= '';
58855         }
58856     }
58857     if (this.eventStore) {
58858         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58859         this.eventStore.on('load',this.onLoad, this);
58860         this.eventStore.on('beforeload',this.clearEvents, this);
58861          
58862     }
58863     
58864     this.dataSource = new Roo.data.Store({
58865             proxy: new Roo.data.MemoryProxy(rows),
58866             reader: new Roo.data.ArrayReader({}, [
58867                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58868     });
58869
58870     this.dataSource.load();
58871     this.ds = this.dataSource;
58872     this.ds.xmodule = this.xmodule || false;
58873     
58874     
58875     var cellRender = function(v,x,r)
58876     {
58877         return String.format(
58878             '<div class="fc-day  fc-widget-content"><div>' +
58879                 '<div class="fc-event-container"></div>' +
58880                 '<div class="fc-day-number">{0}</div>'+
58881                 
58882                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58883             '</div></div>', v);
58884     
58885     }
58886     
58887     
58888     this.colModel = new Roo.grid.ColumnModel( [
58889         {
58890             xtype: 'ColumnModel',
58891             xns: Roo.grid,
58892             dataIndex : 'weekday0',
58893             header : 'Sunday',
58894             renderer : cellRender
58895         },
58896         {
58897             xtype: 'ColumnModel',
58898             xns: Roo.grid,
58899             dataIndex : 'weekday1',
58900             header : 'Monday',
58901             renderer : cellRender
58902         },
58903         {
58904             xtype: 'ColumnModel',
58905             xns: Roo.grid,
58906             dataIndex : 'weekday2',
58907             header : 'Tuesday',
58908             renderer : cellRender
58909         },
58910         {
58911             xtype: 'ColumnModel',
58912             xns: Roo.grid,
58913             dataIndex : 'weekday3',
58914             header : 'Wednesday',
58915             renderer : cellRender
58916         },
58917         {
58918             xtype: 'ColumnModel',
58919             xns: Roo.grid,
58920             dataIndex : 'weekday4',
58921             header : 'Thursday',
58922             renderer : cellRender
58923         },
58924         {
58925             xtype: 'ColumnModel',
58926             xns: Roo.grid,
58927             dataIndex : 'weekday5',
58928             header : 'Friday',
58929             renderer : cellRender
58930         },
58931         {
58932             xtype: 'ColumnModel',
58933             xns: Roo.grid,
58934             dataIndex : 'weekday6',
58935             header : 'Saturday',
58936             renderer : cellRender
58937         }
58938     ]);
58939     this.cm = this.colModel;
58940     this.cm.xmodule = this.xmodule || false;
58941  
58942         
58943           
58944     //this.selModel = new Roo.grid.CellSelectionModel();
58945     //this.sm = this.selModel;
58946     //this.selModel.init(this);
58947     
58948     
58949     if(this.width){
58950         this.container.setWidth(this.width);
58951     }
58952
58953     if(this.height){
58954         this.container.setHeight(this.height);
58955     }
58956     /** @private */
58957         this.addEvents({
58958         // raw events
58959         /**
58960          * @event click
58961          * The raw click event for the entire grid.
58962          * @param {Roo.EventObject} e
58963          */
58964         "click" : true,
58965         /**
58966          * @event dblclick
58967          * The raw dblclick event for the entire grid.
58968          * @param {Roo.EventObject} e
58969          */
58970         "dblclick" : true,
58971         /**
58972          * @event contextmenu
58973          * The raw contextmenu event for the entire grid.
58974          * @param {Roo.EventObject} e
58975          */
58976         "contextmenu" : true,
58977         /**
58978          * @event mousedown
58979          * The raw mousedown event for the entire grid.
58980          * @param {Roo.EventObject} e
58981          */
58982         "mousedown" : true,
58983         /**
58984          * @event mouseup
58985          * The raw mouseup event for the entire grid.
58986          * @param {Roo.EventObject} e
58987          */
58988         "mouseup" : true,
58989         /**
58990          * @event mouseover
58991          * The raw mouseover event for the entire grid.
58992          * @param {Roo.EventObject} e
58993          */
58994         "mouseover" : true,
58995         /**
58996          * @event mouseout
58997          * The raw mouseout event for the entire grid.
58998          * @param {Roo.EventObject} e
58999          */
59000         "mouseout" : true,
59001         /**
59002          * @event keypress
59003          * The raw keypress event for the entire grid.
59004          * @param {Roo.EventObject} e
59005          */
59006         "keypress" : true,
59007         /**
59008          * @event keydown
59009          * The raw keydown event for the entire grid.
59010          * @param {Roo.EventObject} e
59011          */
59012         "keydown" : true,
59013
59014         // custom events
59015
59016         /**
59017          * @event cellclick
59018          * Fires when a cell is clicked
59019          * @param {Grid} this
59020          * @param {Number} rowIndex
59021          * @param {Number} columnIndex
59022          * @param {Roo.EventObject} e
59023          */
59024         "cellclick" : true,
59025         /**
59026          * @event celldblclick
59027          * Fires when a cell is double clicked
59028          * @param {Grid} this
59029          * @param {Number} rowIndex
59030          * @param {Number} columnIndex
59031          * @param {Roo.EventObject} e
59032          */
59033         "celldblclick" : true,
59034         /**
59035          * @event rowclick
59036          * Fires when a row is clicked
59037          * @param {Grid} this
59038          * @param {Number} rowIndex
59039          * @param {Roo.EventObject} e
59040          */
59041         "rowclick" : true,
59042         /**
59043          * @event rowdblclick
59044          * Fires when a row is double clicked
59045          * @param {Grid} this
59046          * @param {Number} rowIndex
59047          * @param {Roo.EventObject} e
59048          */
59049         "rowdblclick" : true,
59050         /**
59051          * @event headerclick
59052          * Fires when a header is clicked
59053          * @param {Grid} this
59054          * @param {Number} columnIndex
59055          * @param {Roo.EventObject} e
59056          */
59057         "headerclick" : true,
59058         /**
59059          * @event headerdblclick
59060          * Fires when a header cell is double clicked
59061          * @param {Grid} this
59062          * @param {Number} columnIndex
59063          * @param {Roo.EventObject} e
59064          */
59065         "headerdblclick" : true,
59066         /**
59067          * @event rowcontextmenu
59068          * Fires when a row is right clicked
59069          * @param {Grid} this
59070          * @param {Number} rowIndex
59071          * @param {Roo.EventObject} e
59072          */
59073         "rowcontextmenu" : true,
59074         /**
59075          * @event cellcontextmenu
59076          * Fires when a cell is right clicked
59077          * @param {Grid} this
59078          * @param {Number} rowIndex
59079          * @param {Number} cellIndex
59080          * @param {Roo.EventObject} e
59081          */
59082          "cellcontextmenu" : true,
59083         /**
59084          * @event headercontextmenu
59085          * Fires when a header is right clicked
59086          * @param {Grid} this
59087          * @param {Number} columnIndex
59088          * @param {Roo.EventObject} e
59089          */
59090         "headercontextmenu" : true,
59091         /**
59092          * @event bodyscroll
59093          * Fires when the body element is scrolled
59094          * @param {Number} scrollLeft
59095          * @param {Number} scrollTop
59096          */
59097         "bodyscroll" : true,
59098         /**
59099          * @event columnresize
59100          * Fires when the user resizes a column
59101          * @param {Number} columnIndex
59102          * @param {Number} newSize
59103          */
59104         "columnresize" : true,
59105         /**
59106          * @event columnmove
59107          * Fires when the user moves a column
59108          * @param {Number} oldIndex
59109          * @param {Number} newIndex
59110          */
59111         "columnmove" : true,
59112         /**
59113          * @event startdrag
59114          * Fires when row(s) start being dragged
59115          * @param {Grid} this
59116          * @param {Roo.GridDD} dd The drag drop object
59117          * @param {event} e The raw browser event
59118          */
59119         "startdrag" : true,
59120         /**
59121          * @event enddrag
59122          * Fires when a drag operation is complete
59123          * @param {Grid} this
59124          * @param {Roo.GridDD} dd The drag drop object
59125          * @param {event} e The raw browser event
59126          */
59127         "enddrag" : true,
59128         /**
59129          * @event dragdrop
59130          * Fires when dragged row(s) are dropped on a valid DD target
59131          * @param {Grid} this
59132          * @param {Roo.GridDD} dd The drag drop object
59133          * @param {String} targetId The target drag drop object
59134          * @param {event} e The raw browser event
59135          */
59136         "dragdrop" : true,
59137         /**
59138          * @event dragover
59139          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59140          * @param {Grid} this
59141          * @param {Roo.GridDD} dd The drag drop object
59142          * @param {String} targetId The target drag drop object
59143          * @param {event} e The raw browser event
59144          */
59145         "dragover" : true,
59146         /**
59147          * @event dragenter
59148          *  Fires when the dragged row(s) first cross another DD target while being dragged
59149          * @param {Grid} this
59150          * @param {Roo.GridDD} dd The drag drop object
59151          * @param {String} targetId The target drag drop object
59152          * @param {event} e The raw browser event
59153          */
59154         "dragenter" : true,
59155         /**
59156          * @event dragout
59157          * Fires when the dragged row(s) leave another DD target while being dragged
59158          * @param {Grid} this
59159          * @param {Roo.GridDD} dd The drag drop object
59160          * @param {String} targetId The target drag drop object
59161          * @param {event} e The raw browser event
59162          */
59163         "dragout" : true,
59164         /**
59165          * @event rowclass
59166          * Fires when a row is rendered, so you can change add a style to it.
59167          * @param {GridView} gridview   The grid view
59168          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59169          */
59170         'rowclass' : true,
59171
59172         /**
59173          * @event render
59174          * Fires when the grid is rendered
59175          * @param {Grid} grid
59176          */
59177         'render' : true,
59178             /**
59179              * @event select
59180              * Fires when a date is selected
59181              * @param {DatePicker} this
59182              * @param {Date} date The selected date
59183              */
59184         'select': true,
59185         /**
59186              * @event monthchange
59187              * Fires when the displayed month changes 
59188              * @param {DatePicker} this
59189              * @param {Date} date The selected month
59190              */
59191         'monthchange': true,
59192         /**
59193              * @event evententer
59194              * Fires when mouse over an event
59195              * @param {Calendar} this
59196              * @param {event} Event
59197              */
59198         'evententer': true,
59199         /**
59200              * @event eventleave
59201              * Fires when the mouse leaves an
59202              * @param {Calendar} this
59203              * @param {event}
59204              */
59205         'eventleave': true,
59206         /**
59207              * @event eventclick
59208              * Fires when the mouse click an
59209              * @param {Calendar} this
59210              * @param {event}
59211              */
59212         'eventclick': true,
59213         /**
59214              * @event eventrender
59215              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59216              * @param {Calendar} this
59217              * @param {data} data to be modified
59218              */
59219         'eventrender': true
59220         
59221     });
59222
59223     Roo.grid.Grid.superclass.constructor.call(this);
59224     this.on('render', function() {
59225         this.view.el.addClass('x-grid-cal'); 
59226         
59227         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59228
59229     },this);
59230     
59231     if (!Roo.grid.Calendar.style) {
59232         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59233             
59234             
59235             '.x-grid-cal .x-grid-col' :  {
59236                 height: 'auto !important',
59237                 'vertical-align': 'top'
59238             },
59239             '.x-grid-cal  .fc-event-hori' : {
59240                 height: '14px'
59241             }
59242              
59243             
59244         }, Roo.id());
59245     }
59246
59247     
59248     
59249 };
59250 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59251     /**
59252      * @cfg {Store} eventStore The store that loads events.
59253      */
59254     eventStore : 25,
59255
59256      
59257     activeDate : false,
59258     startDay : 0,
59259     autoWidth : true,
59260     monitorWindowResize : false,
59261
59262     
59263     resizeColumns : function() {
59264         var col = (this.view.el.getWidth() / 7) - 3;
59265         // loop through cols, and setWidth
59266         for(var i =0 ; i < 7 ; i++){
59267             this.cm.setColumnWidth(i, col);
59268         }
59269     },
59270      setDate :function(date) {
59271         
59272         Roo.log('setDate?');
59273         
59274         this.resizeColumns();
59275         var vd = this.activeDate;
59276         this.activeDate = date;
59277 //        if(vd && this.el){
59278 //            var t = date.getTime();
59279 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59280 //                Roo.log('using add remove');
59281 //                
59282 //                this.fireEvent('monthchange', this, date);
59283 //                
59284 //                this.cells.removeClass("fc-state-highlight");
59285 //                this.cells.each(function(c){
59286 //                   if(c.dateValue == t){
59287 //                       c.addClass("fc-state-highlight");
59288 //                       setTimeout(function(){
59289 //                            try{c.dom.firstChild.focus();}catch(e){}
59290 //                       }, 50);
59291 //                       return false;
59292 //                   }
59293 //                   return true;
59294 //                });
59295 //                return;
59296 //            }
59297 //        }
59298         
59299         var days = date.getDaysInMonth();
59300         
59301         var firstOfMonth = date.getFirstDateOfMonth();
59302         var startingPos = firstOfMonth.getDay()-this.startDay;
59303         
59304         if(startingPos < this.startDay){
59305             startingPos += 7;
59306         }
59307         
59308         var pm = date.add(Date.MONTH, -1);
59309         var prevStart = pm.getDaysInMonth()-startingPos;
59310 //        
59311         
59312         
59313         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59314         
59315         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59316         //this.cells.addClassOnOver('fc-state-hover');
59317         
59318         var cells = this.cells.elements;
59319         var textEls = this.textNodes;
59320         
59321         //Roo.each(cells, function(cell){
59322         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59323         //});
59324         
59325         days += startingPos;
59326
59327         // convert everything to numbers so it's fast
59328         var day = 86400000;
59329         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59330         //Roo.log(d);
59331         //Roo.log(pm);
59332         //Roo.log(prevStart);
59333         
59334         var today = new Date().clearTime().getTime();
59335         var sel = date.clearTime().getTime();
59336         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59337         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59338         var ddMatch = this.disabledDatesRE;
59339         var ddText = this.disabledDatesText;
59340         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59341         var ddaysText = this.disabledDaysText;
59342         var format = this.format;
59343         
59344         var setCellClass = function(cal, cell){
59345             
59346             //Roo.log('set Cell Class');
59347             cell.title = "";
59348             var t = d.getTime();
59349             
59350             //Roo.log(d);
59351             
59352             
59353             cell.dateValue = t;
59354             if(t == today){
59355                 cell.className += " fc-today";
59356                 cell.className += " fc-state-highlight";
59357                 cell.title = cal.todayText;
59358             }
59359             if(t == sel){
59360                 // disable highlight in other month..
59361                 cell.className += " fc-state-highlight";
59362                 
59363             }
59364             // disabling
59365             if(t < min) {
59366                 //cell.className = " fc-state-disabled";
59367                 cell.title = cal.minText;
59368                 return;
59369             }
59370             if(t > max) {
59371                 //cell.className = " fc-state-disabled";
59372                 cell.title = cal.maxText;
59373                 return;
59374             }
59375             if(ddays){
59376                 if(ddays.indexOf(d.getDay()) != -1){
59377                     // cell.title = ddaysText;
59378                    // cell.className = " fc-state-disabled";
59379                 }
59380             }
59381             if(ddMatch && format){
59382                 var fvalue = d.dateFormat(format);
59383                 if(ddMatch.test(fvalue)){
59384                     cell.title = ddText.replace("%0", fvalue);
59385                    cell.className = " fc-state-disabled";
59386                 }
59387             }
59388             
59389             if (!cell.initialClassName) {
59390                 cell.initialClassName = cell.dom.className;
59391             }
59392             
59393             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59394         };
59395
59396         var i = 0;
59397         
59398         for(; i < startingPos; i++) {
59399             cells[i].dayName =  (++prevStart);
59400             Roo.log(textEls[i]);
59401             d.setDate(d.getDate()+1);
59402             
59403             //cells[i].className = "fc-past fc-other-month";
59404             setCellClass(this, cells[i]);
59405         }
59406         
59407         var intDay = 0;
59408         
59409         for(; i < days; i++){
59410             intDay = i - startingPos + 1;
59411             cells[i].dayName =  (intDay);
59412             d.setDate(d.getDate()+1);
59413             
59414             cells[i].className = ''; // "x-date-active";
59415             setCellClass(this, cells[i]);
59416         }
59417         var extraDays = 0;
59418         
59419         for(; i < 42; i++) {
59420             //textEls[i].innerHTML = (++extraDays);
59421             
59422             d.setDate(d.getDate()+1);
59423             cells[i].dayName = (++extraDays);
59424             cells[i].className = "fc-future fc-other-month";
59425             setCellClass(this, cells[i]);
59426         }
59427         
59428         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59429         
59430         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59431         
59432         // this will cause all the cells to mis
59433         var rows= [];
59434         var i =0;
59435         for (var r = 0;r < 6;r++) {
59436             for (var c =0;c < 7;c++) {
59437                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59438             }    
59439         }
59440         
59441         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59442         for(i=0;i<cells.length;i++) {
59443             
59444             this.cells.elements[i].dayName = cells[i].dayName ;
59445             this.cells.elements[i].className = cells[i].className;
59446             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59447             this.cells.elements[i].title = cells[i].title ;
59448             this.cells.elements[i].dateValue = cells[i].dateValue ;
59449         }
59450         
59451         
59452         
59453         
59454         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59455         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59456         
59457         ////if(totalRows != 6){
59458             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59459            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59460        // }
59461         
59462         this.fireEvent('monthchange', this, date);
59463         
59464         
59465     },
59466  /**
59467      * Returns the grid's SelectionModel.
59468      * @return {SelectionModel}
59469      */
59470     getSelectionModel : function(){
59471         if(!this.selModel){
59472             this.selModel = new Roo.grid.CellSelectionModel();
59473         }
59474         return this.selModel;
59475     },
59476
59477     load: function() {
59478         this.eventStore.load()
59479         
59480         
59481         
59482     },
59483     
59484     findCell : function(dt) {
59485         dt = dt.clearTime().getTime();
59486         var ret = false;
59487         this.cells.each(function(c){
59488             //Roo.log("check " +c.dateValue + '?=' + dt);
59489             if(c.dateValue == dt){
59490                 ret = c;
59491                 return false;
59492             }
59493             return true;
59494         });
59495         
59496         return ret;
59497     },
59498     
59499     findCells : function(rec) {
59500         var s = rec.data.start_dt.clone().clearTime().getTime();
59501        // Roo.log(s);
59502         var e= rec.data.end_dt.clone().clearTime().getTime();
59503        // Roo.log(e);
59504         var ret = [];
59505         this.cells.each(function(c){
59506              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59507             
59508             if(c.dateValue > e){
59509                 return ;
59510             }
59511             if(c.dateValue < s){
59512                 return ;
59513             }
59514             ret.push(c);
59515         });
59516         
59517         return ret;    
59518     },
59519     
59520     findBestRow: function(cells)
59521     {
59522         var ret = 0;
59523         
59524         for (var i =0 ; i < cells.length;i++) {
59525             ret  = Math.max(cells[i].rows || 0,ret);
59526         }
59527         return ret;
59528         
59529     },
59530     
59531     
59532     addItem : function(rec)
59533     {
59534         // look for vertical location slot in
59535         var cells = this.findCells(rec);
59536         
59537         rec.row = this.findBestRow(cells);
59538         
59539         // work out the location.
59540         
59541         var crow = false;
59542         var rows = [];
59543         for(var i =0; i < cells.length; i++) {
59544             if (!crow) {
59545                 crow = {
59546                     start : cells[i],
59547                     end :  cells[i]
59548                 };
59549                 continue;
59550             }
59551             if (crow.start.getY() == cells[i].getY()) {
59552                 // on same row.
59553                 crow.end = cells[i];
59554                 continue;
59555             }
59556             // different row.
59557             rows.push(crow);
59558             crow = {
59559                 start: cells[i],
59560                 end : cells[i]
59561             };
59562             
59563         }
59564         
59565         rows.push(crow);
59566         rec.els = [];
59567         rec.rows = rows;
59568         rec.cells = cells;
59569         for (var i = 0; i < cells.length;i++) {
59570             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59571             
59572         }
59573         
59574         
59575     },
59576     
59577     clearEvents: function() {
59578         
59579         if (!this.eventStore.getCount()) {
59580             return;
59581         }
59582         // reset number of rows in cells.
59583         Roo.each(this.cells.elements, function(c){
59584             c.rows = 0;
59585         });
59586         
59587         this.eventStore.each(function(e) {
59588             this.clearEvent(e);
59589         },this);
59590         
59591     },
59592     
59593     clearEvent : function(ev)
59594     {
59595         if (ev.els) {
59596             Roo.each(ev.els, function(el) {
59597                 el.un('mouseenter' ,this.onEventEnter, this);
59598                 el.un('mouseleave' ,this.onEventLeave, this);
59599                 el.remove();
59600             },this);
59601             ev.els = [];
59602         }
59603     },
59604     
59605     
59606     renderEvent : function(ev,ctr) {
59607         if (!ctr) {
59608              ctr = this.view.el.select('.fc-event-container',true).first();
59609         }
59610         
59611          
59612         this.clearEvent(ev);
59613             //code
59614        
59615         
59616         
59617         ev.els = [];
59618         var cells = ev.cells;
59619         var rows = ev.rows;
59620         this.fireEvent('eventrender', this, ev);
59621         
59622         for(var i =0; i < rows.length; i++) {
59623             
59624             cls = '';
59625             if (i == 0) {
59626                 cls += ' fc-event-start';
59627             }
59628             if ((i+1) == rows.length) {
59629                 cls += ' fc-event-end';
59630             }
59631             
59632             //Roo.log(ev.data);
59633             // how many rows should it span..
59634             var cg = this.eventTmpl.append(ctr,Roo.apply({
59635                 fccls : cls
59636                 
59637             }, ev.data) , true);
59638             
59639             
59640             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59641             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59642             cg.on('click', this.onEventClick, this, ev);
59643             
59644             ev.els.push(cg);
59645             
59646             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59647             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59648             //Roo.log(cg);
59649              
59650             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59651             cg.setWidth(ebox.right - sbox.x -2);
59652         }
59653     },
59654     
59655     renderEvents: function()
59656     {   
59657         // first make sure there is enough space..
59658         
59659         if (!this.eventTmpl) {
59660             this.eventTmpl = new Roo.Template(
59661                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59662                     '<div class="fc-event-inner">' +
59663                         '<span class="fc-event-time">{time}</span>' +
59664                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59665                     '</div>' +
59666                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59667                 '</div>'
59668             );
59669                 
59670         }
59671                
59672         
59673         
59674         this.cells.each(function(c) {
59675             //Roo.log(c.select('.fc-day-content div',true).first());
59676             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59677         });
59678         
59679         var ctr = this.view.el.select('.fc-event-container',true).first();
59680         
59681         var cls;
59682         this.eventStore.each(function(ev){
59683             
59684             this.renderEvent(ev);
59685              
59686              
59687         }, this);
59688         this.view.layout();
59689         
59690     },
59691     
59692     onEventEnter: function (e, el,event,d) {
59693         this.fireEvent('evententer', this, el, event);
59694     },
59695     
59696     onEventLeave: function (e, el,event,d) {
59697         this.fireEvent('eventleave', this, el, event);
59698     },
59699     
59700     onEventClick: function (e, el,event,d) {
59701         this.fireEvent('eventclick', this, el, event);
59702     },
59703     
59704     onMonthChange: function () {
59705         this.store.load();
59706     },
59707     
59708     onLoad: function () {
59709         
59710         //Roo.log('calendar onload');
59711 //         
59712         if(this.eventStore.getCount() > 0){
59713             
59714            
59715             
59716             this.eventStore.each(function(d){
59717                 
59718                 
59719                 // FIXME..
59720                 var add =   d.data;
59721                 if (typeof(add.end_dt) == 'undefined')  {
59722                     Roo.log("Missing End time in calendar data: ");
59723                     Roo.log(d);
59724                     return;
59725                 }
59726                 if (typeof(add.start_dt) == 'undefined')  {
59727                     Roo.log("Missing Start time in calendar data: ");
59728                     Roo.log(d);
59729                     return;
59730                 }
59731                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59732                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59733                 add.id = add.id || d.id;
59734                 add.title = add.title || '??';
59735                 
59736                 this.addItem(d);
59737                 
59738              
59739             },this);
59740         }
59741         
59742         this.renderEvents();
59743     }
59744     
59745
59746 });
59747 /*
59748  grid : {
59749                 xtype: 'Grid',
59750                 xns: Roo.grid,
59751                 listeners : {
59752                     render : function ()
59753                     {
59754                         _this.grid = this;
59755                         
59756                         if (!this.view.el.hasClass('course-timesheet')) {
59757                             this.view.el.addClass('course-timesheet');
59758                         }
59759                         if (this.tsStyle) {
59760                             this.ds.load({});
59761                             return; 
59762                         }
59763                         Roo.log('width');
59764                         Roo.log(_this.grid.view.el.getWidth());
59765                         
59766                         
59767                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59768                             '.course-timesheet .x-grid-row' : {
59769                                 height: '80px'
59770                             },
59771                             '.x-grid-row td' : {
59772                                 'vertical-align' : 0
59773                             },
59774                             '.course-edit-link' : {
59775                                 'color' : 'blue',
59776                                 'text-overflow' : 'ellipsis',
59777                                 'overflow' : 'hidden',
59778                                 'white-space' : 'nowrap',
59779                                 'cursor' : 'pointer'
59780                             },
59781                             '.sub-link' : {
59782                                 'color' : 'green'
59783                             },
59784                             '.de-act-sup-link' : {
59785                                 'color' : 'purple',
59786                                 'text-decoration' : 'line-through'
59787                             },
59788                             '.de-act-link' : {
59789                                 'color' : 'red',
59790                                 'text-decoration' : 'line-through'
59791                             },
59792                             '.course-timesheet .course-highlight' : {
59793                                 'border-top-style': 'dashed !important',
59794                                 'border-bottom-bottom': 'dashed !important'
59795                             },
59796                             '.course-timesheet .course-item' : {
59797                                 'font-family'   : 'tahoma, arial, helvetica',
59798                                 'font-size'     : '11px',
59799                                 'overflow'      : 'hidden',
59800                                 'padding-left'  : '10px',
59801                                 'padding-right' : '10px',
59802                                 'padding-top' : '10px' 
59803                             }
59804                             
59805                         }, Roo.id());
59806                                 this.ds.load({});
59807                     }
59808                 },
59809                 autoWidth : true,
59810                 monitorWindowResize : false,
59811                 cellrenderer : function(v,x,r)
59812                 {
59813                     return v;
59814                 },
59815                 sm : {
59816                     xtype: 'CellSelectionModel',
59817                     xns: Roo.grid
59818                 },
59819                 dataSource : {
59820                     xtype: 'Store',
59821                     xns: Roo.data,
59822                     listeners : {
59823                         beforeload : function (_self, options)
59824                         {
59825                             options.params = options.params || {};
59826                             options.params._month = _this.monthField.getValue();
59827                             options.params.limit = 9999;
59828                             options.params['sort'] = 'when_dt';    
59829                             options.params['dir'] = 'ASC';    
59830                             this.proxy.loadResponse = this.loadResponse;
59831                             Roo.log("load?");
59832                             //this.addColumns();
59833                         },
59834                         load : function (_self, records, options)
59835                         {
59836                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59837                                 // if you click on the translation.. you can edit it...
59838                                 var el = Roo.get(this);
59839                                 var id = el.dom.getAttribute('data-id');
59840                                 var d = el.dom.getAttribute('data-date');
59841                                 var t = el.dom.getAttribute('data-time');
59842                                 //var id = this.child('span').dom.textContent;
59843                                 
59844                                 //Roo.log(this);
59845                                 Pman.Dialog.CourseCalendar.show({
59846                                     id : id,
59847                                     when_d : d,
59848                                     when_t : t,
59849                                     productitem_active : id ? 1 : 0
59850                                 }, function() {
59851                                     _this.grid.ds.load({});
59852                                 });
59853                            
59854                            });
59855                            
59856                            _this.panel.fireEvent('resize', [ '', '' ]);
59857                         }
59858                     },
59859                     loadResponse : function(o, success, response){
59860                             // this is overridden on before load..
59861                             
59862                             Roo.log("our code?");       
59863                             //Roo.log(success);
59864                             //Roo.log(response)
59865                             delete this.activeRequest;
59866                             if(!success){
59867                                 this.fireEvent("loadexception", this, o, response);
59868                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59869                                 return;
59870                             }
59871                             var result;
59872                             try {
59873                                 result = o.reader.read(response);
59874                             }catch(e){
59875                                 Roo.log("load exception?");
59876                                 this.fireEvent("loadexception", this, o, response, e);
59877                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59878                                 return;
59879                             }
59880                             Roo.log("ready...");        
59881                             // loop through result.records;
59882                             // and set this.tdate[date] = [] << array of records..
59883                             _this.tdata  = {};
59884                             Roo.each(result.records, function(r){
59885                                 //Roo.log(r.data);
59886                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59887                                     _this.tdata[r.data.when_dt.format('j')] = [];
59888                                 }
59889                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59890                             });
59891                             
59892                             //Roo.log(_this.tdata);
59893                             
59894                             result.records = [];
59895                             result.totalRecords = 6;
59896                     
59897                             // let's generate some duumy records for the rows.
59898                             //var st = _this.dateField.getValue();
59899                             
59900                             // work out monday..
59901                             //st = st.add(Date.DAY, -1 * st.format('w'));
59902                             
59903                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59904                             
59905                             var firstOfMonth = date.getFirstDayOfMonth();
59906                             var days = date.getDaysInMonth();
59907                             var d = 1;
59908                             var firstAdded = false;
59909                             for (var i = 0; i < result.totalRecords ; i++) {
59910                                 //var d= st.add(Date.DAY, i);
59911                                 var row = {};
59912                                 var added = 0;
59913                                 for(var w = 0 ; w < 7 ; w++){
59914                                     if(!firstAdded && firstOfMonth != w){
59915                                         continue;
59916                                     }
59917                                     if(d > days){
59918                                         continue;
59919                                     }
59920                                     firstAdded = true;
59921                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59922                                     row['weekday'+w] = String.format(
59923                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59924                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59925                                                     d,
59926                                                     date.format('Y-m-')+dd
59927                                                 );
59928                                     added++;
59929                                     if(typeof(_this.tdata[d]) != 'undefined'){
59930                                         Roo.each(_this.tdata[d], function(r){
59931                                             var is_sub = '';
59932                                             var deactive = '';
59933                                             var id = r.id;
59934                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59935                                             if(r.parent_id*1>0){
59936                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59937                                                 id = r.parent_id;
59938                                             }
59939                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59940                                                 deactive = 'de-act-link';
59941                                             }
59942                                             
59943                                             row['weekday'+w] += String.format(
59944                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59945                                                     id, //0
59946                                                     r.product_id_name, //1
59947                                                     r.when_dt.format('h:ia'), //2
59948                                                     is_sub, //3
59949                                                     deactive, //4
59950                                                     desc // 5
59951                                             );
59952                                         });
59953                                     }
59954                                     d++;
59955                                 }
59956                                 
59957                                 // only do this if something added..
59958                                 if(added > 0){ 
59959                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
59960                                 }
59961                                 
59962                                 
59963                                 // push it twice. (second one with an hour..
59964                                 
59965                             }
59966                             //Roo.log(result);
59967                             this.fireEvent("load", this, o, o.request.arg);
59968                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
59969                         },
59970                     sortInfo : {field: 'when_dt', direction : 'ASC' },
59971                     proxy : {
59972                         xtype: 'HttpProxy',
59973                         xns: Roo.data,
59974                         method : 'GET',
59975                         url : baseURL + '/Roo/Shop_course.php'
59976                     },
59977                     reader : {
59978                         xtype: 'JsonReader',
59979                         xns: Roo.data,
59980                         id : 'id',
59981                         fields : [
59982                             {
59983                                 'name': 'id',
59984                                 'type': 'int'
59985                             },
59986                             {
59987                                 'name': 'when_dt',
59988                                 'type': 'string'
59989                             },
59990                             {
59991                                 'name': 'end_dt',
59992                                 'type': 'string'
59993                             },
59994                             {
59995                                 'name': 'parent_id',
59996                                 'type': 'int'
59997                             },
59998                             {
59999                                 'name': 'product_id',
60000                                 'type': 'int'
60001                             },
60002                             {
60003                                 'name': 'productitem_id',
60004                                 'type': 'int'
60005                             },
60006                             {
60007                                 'name': 'guid',
60008                                 'type': 'int'
60009                             }
60010                         ]
60011                     }
60012                 },
60013                 toolbar : {
60014                     xtype: 'Toolbar',
60015                     xns: Roo,
60016                     items : [
60017                         {
60018                             xtype: 'Button',
60019                             xns: Roo.Toolbar,
60020                             listeners : {
60021                                 click : function (_self, e)
60022                                 {
60023                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60024                                     sd.setMonth(sd.getMonth()-1);
60025                                     _this.monthField.setValue(sd.format('Y-m-d'));
60026                                     _this.grid.ds.load({});
60027                                 }
60028                             },
60029                             text : "Back"
60030                         },
60031                         {
60032                             xtype: 'Separator',
60033                             xns: Roo.Toolbar
60034                         },
60035                         {
60036                             xtype: 'MonthField',
60037                             xns: Roo.form,
60038                             listeners : {
60039                                 render : function (_self)
60040                                 {
60041                                     _this.monthField = _self;
60042                                    // _this.monthField.set  today
60043                                 },
60044                                 select : function (combo, date)
60045                                 {
60046                                     _this.grid.ds.load({});
60047                                 }
60048                             },
60049                             value : (function() { return new Date(); })()
60050                         },
60051                         {
60052                             xtype: 'Separator',
60053                             xns: Roo.Toolbar
60054                         },
60055                         {
60056                             xtype: 'TextItem',
60057                             xns: Roo.Toolbar,
60058                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60059                         },
60060                         {
60061                             xtype: 'Fill',
60062                             xns: Roo.Toolbar
60063                         },
60064                         {
60065                             xtype: 'Button',
60066                             xns: Roo.Toolbar,
60067                             listeners : {
60068                                 click : function (_self, e)
60069                                 {
60070                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60071                                     sd.setMonth(sd.getMonth()+1);
60072                                     _this.monthField.setValue(sd.format('Y-m-d'));
60073                                     _this.grid.ds.load({});
60074                                 }
60075                             },
60076                             text : "Next"
60077                         }
60078                     ]
60079                 },
60080                  
60081             }
60082         };
60083         
60084         *//*
60085  * Based on:
60086  * Ext JS Library 1.1.1
60087  * Copyright(c) 2006-2007, Ext JS, LLC.
60088  *
60089  * Originally Released Under LGPL - original licence link has changed is not relivant.
60090  *
60091  * Fork - LGPL
60092  * <script type="text/javascript">
60093  */
60094  
60095 /**
60096  * @class Roo.LoadMask
60097  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60098  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60099  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60100  * element's UpdateManager load indicator and will be destroyed after the initial load.
60101  * @constructor
60102  * Create a new LoadMask
60103  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60104  * @param {Object} config The config object
60105  */
60106 Roo.LoadMask = function(el, config){
60107     this.el = Roo.get(el);
60108     Roo.apply(this, config);
60109     if(this.store){
60110         this.store.on('beforeload', this.onBeforeLoad, this);
60111         this.store.on('load', this.onLoad, this);
60112         this.store.on('loadexception', this.onLoadException, this);
60113         this.removeMask = false;
60114     }else{
60115         var um = this.el.getUpdateManager();
60116         um.showLoadIndicator = false; // disable the default indicator
60117         um.on('beforeupdate', this.onBeforeLoad, this);
60118         um.on('update', this.onLoad, this);
60119         um.on('failure', this.onLoad, this);
60120         this.removeMask = true;
60121     }
60122 };
60123
60124 Roo.LoadMask.prototype = {
60125     /**
60126      * @cfg {Boolean} removeMask
60127      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60128      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60129      */
60130     /**
60131      * @cfg {String} msg
60132      * The text to display in a centered loading message box (defaults to 'Loading...')
60133      */
60134     msg : 'Loading...',
60135     /**
60136      * @cfg {String} msgCls
60137      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60138      */
60139     msgCls : 'x-mask-loading',
60140
60141     /**
60142      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60143      * @type Boolean
60144      */
60145     disabled: false,
60146
60147     /**
60148      * Disables the mask to prevent it from being displayed
60149      */
60150     disable : function(){
60151        this.disabled = true;
60152     },
60153
60154     /**
60155      * Enables the mask so that it can be displayed
60156      */
60157     enable : function(){
60158         this.disabled = false;
60159     },
60160     
60161     onLoadException : function()
60162     {
60163         Roo.log(arguments);
60164         
60165         if (typeof(arguments[3]) != 'undefined') {
60166             Roo.MessageBox.alert("Error loading",arguments[3]);
60167         } 
60168         /*
60169         try {
60170             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60171                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60172             }   
60173         } catch(e) {
60174             
60175         }
60176         */
60177     
60178         
60179         
60180         this.el.unmask(this.removeMask);
60181     },
60182     // private
60183     onLoad : function()
60184     {
60185         this.el.unmask(this.removeMask);
60186     },
60187
60188     // private
60189     onBeforeLoad : function(){
60190         if(!this.disabled){
60191             this.el.mask(this.msg, this.msgCls);
60192         }
60193     },
60194
60195     // private
60196     destroy : function(){
60197         if(this.store){
60198             this.store.un('beforeload', this.onBeforeLoad, this);
60199             this.store.un('load', this.onLoad, this);
60200             this.store.un('loadexception', this.onLoadException, this);
60201         }else{
60202             var um = this.el.getUpdateManager();
60203             um.un('beforeupdate', this.onBeforeLoad, this);
60204             um.un('update', this.onLoad, this);
60205             um.un('failure', this.onLoad, this);
60206         }
60207     }
60208 };/*
60209  * Based on:
60210  * Ext JS Library 1.1.1
60211  * Copyright(c) 2006-2007, Ext JS, LLC.
60212  *
60213  * Originally Released Under LGPL - original licence link has changed is not relivant.
60214  *
60215  * Fork - LGPL
60216  * <script type="text/javascript">
60217  */
60218
60219
60220 /**
60221  * @class Roo.XTemplate
60222  * @extends Roo.Template
60223  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60224 <pre><code>
60225 var t = new Roo.XTemplate(
60226         '&lt;select name="{name}"&gt;',
60227                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60228         '&lt;/select&gt;'
60229 );
60230  
60231 // then append, applying the master template values
60232  </code></pre>
60233  *
60234  * Supported features:
60235  *
60236  *  Tags:
60237
60238 <pre><code>
60239       {a_variable} - output encoded.
60240       {a_variable.format:("Y-m-d")} - call a method on the variable
60241       {a_variable:raw} - unencoded output
60242       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60243       {a_variable:this.method_on_template(...)} - call a method on the template object.
60244  
60245 </code></pre>
60246  *  The tpl tag:
60247 <pre><code>
60248         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60249         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60250         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60251         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60252   
60253         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60254         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60255 </code></pre>
60256  *      
60257  */
60258 Roo.XTemplate = function()
60259 {
60260     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60261     if (this.html) {
60262         this.compile();
60263     }
60264 };
60265
60266
60267 Roo.extend(Roo.XTemplate, Roo.Template, {
60268
60269     /**
60270      * The various sub templates
60271      */
60272     tpls : false,
60273     /**
60274      *
60275      * basic tag replacing syntax
60276      * WORD:WORD()
60277      *
60278      * // you can fake an object call by doing this
60279      *  x.t:(test,tesT) 
60280      * 
60281      */
60282     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60283
60284     /**
60285      * compile the template
60286      *
60287      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60288      *
60289      */
60290     compile: function()
60291     {
60292         var s = this.html;
60293      
60294         s = ['<tpl>', s, '</tpl>'].join('');
60295     
60296         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60297             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60298             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60299             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60300             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60301             m,
60302             id     = 0,
60303             tpls   = [];
60304     
60305         while(true == !!(m = s.match(re))){
60306             var forMatch   = m[0].match(nameRe),
60307                 ifMatch   = m[0].match(ifRe),
60308                 execMatch   = m[0].match(execRe),
60309                 namedMatch   = m[0].match(namedRe),
60310                 
60311                 exp  = null, 
60312                 fn   = null,
60313                 exec = null,
60314                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60315                 
60316             if (ifMatch) {
60317                 // if - puts fn into test..
60318                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60319                 if(exp){
60320                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60321                 }
60322             }
60323             
60324             if (execMatch) {
60325                 // exec - calls a function... returns empty if true is  returned.
60326                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60327                 if(exp){
60328                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60329                 }
60330             }
60331             
60332             
60333             if (name) {
60334                 // for = 
60335                 switch(name){
60336                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60337                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60338                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60339                 }
60340             }
60341             var uid = namedMatch ? namedMatch[1] : id;
60342             
60343             
60344             tpls.push({
60345                 id:     namedMatch ? namedMatch[1] : id,
60346                 target: name,
60347                 exec:   exec,
60348                 test:   fn,
60349                 body:   m[1] || ''
60350             });
60351             if (namedMatch) {
60352                 s = s.replace(m[0], '');
60353             } else { 
60354                 s = s.replace(m[0], '{xtpl'+ id + '}');
60355             }
60356             ++id;
60357         }
60358         this.tpls = [];
60359         for(var i = tpls.length-1; i >= 0; --i){
60360             this.compileTpl(tpls[i]);
60361             this.tpls[tpls[i].id] = tpls[i];
60362         }
60363         this.master = tpls[tpls.length-1];
60364         return this;
60365     },
60366     /**
60367      * same as applyTemplate, except it's done to one of the subTemplates
60368      * when using named templates, you can do:
60369      *
60370      * var str = pl.applySubTemplate('your-name', values);
60371      *
60372      * 
60373      * @param {Number} id of the template
60374      * @param {Object} values to apply to template
60375      * @param {Object} parent (normaly the instance of this object)
60376      */
60377     applySubTemplate : function(id, values, parent)
60378     {
60379         
60380         
60381         var t = this.tpls[id];
60382         
60383         
60384         try { 
60385             if(t.test && !t.test.call(this, values, parent)){
60386                 return '';
60387             }
60388         } catch(e) {
60389             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60390             Roo.log(e.toString());
60391             Roo.log(t.test);
60392             return ''
60393         }
60394         try { 
60395             
60396             if(t.exec && t.exec.call(this, values, parent)){
60397                 return '';
60398             }
60399         } catch(e) {
60400             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60401             Roo.log(e.toString());
60402             Roo.log(t.exec);
60403             return ''
60404         }
60405         try {
60406             var vs = t.target ? t.target.call(this, values, parent) : values;
60407             parent = t.target ? values : parent;
60408             if(t.target && vs instanceof Array){
60409                 var buf = [];
60410                 for(var i = 0, len = vs.length; i < len; i++){
60411                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60412                 }
60413                 return buf.join('');
60414             }
60415             return t.compiled.call(this, vs, parent);
60416         } catch (e) {
60417             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60418             Roo.log(e.toString());
60419             Roo.log(t.compiled);
60420             return '';
60421         }
60422     },
60423
60424     compileTpl : function(tpl)
60425     {
60426         var fm = Roo.util.Format;
60427         var useF = this.disableFormats !== true;
60428         var sep = Roo.isGecko ? "+" : ",";
60429         var undef = function(str) {
60430             Roo.log("Property not found :"  + str);
60431             return '';
60432         };
60433         
60434         var fn = function(m, name, format, args)
60435         {
60436             //Roo.log(arguments);
60437             args = args ? args.replace(/\\'/g,"'") : args;
60438             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60439             if (typeof(format) == 'undefined') {
60440                 format= 'htmlEncode';
60441             }
60442             if (format == 'raw' ) {
60443                 format = false;
60444             }
60445             
60446             if(name.substr(0, 4) == 'xtpl'){
60447                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60448             }
60449             
60450             // build an array of options to determine if value is undefined..
60451             
60452             // basically get 'xxxx.yyyy' then do
60453             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60454             //    (function () { Roo.log("Property not found"); return ''; })() :
60455             //    ......
60456             
60457             var udef_ar = [];
60458             var lookfor = '';
60459             Roo.each(name.split('.'), function(st) {
60460                 lookfor += (lookfor.length ? '.': '') + st;
60461                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60462             });
60463             
60464             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60465             
60466             
60467             if(format && useF){
60468                 
60469                 args = args ? ',' + args : "";
60470                  
60471                 if(format.substr(0, 5) != "this."){
60472                     format = "fm." + format + '(';
60473                 }else{
60474                     format = 'this.call("'+ format.substr(5) + '", ';
60475                     args = ", values";
60476                 }
60477                 
60478                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60479             }
60480              
60481             if (args.length) {
60482                 // called with xxyx.yuu:(test,test)
60483                 // change to ()
60484                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60485             }
60486             // raw.. - :raw modifier..
60487             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60488             
60489         };
60490         var body;
60491         // branched to use + in gecko and [].join() in others
60492         if(Roo.isGecko){
60493             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60494                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60495                     "';};};";
60496         }else{
60497             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60498             body.push(tpl.body.replace(/(\r\n|\n)/g,
60499                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60500             body.push("'].join('');};};");
60501             body = body.join('');
60502         }
60503         
60504         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60505        
60506         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60507         eval(body);
60508         
60509         return this;
60510     },
60511
60512     applyTemplate : function(values){
60513         return this.master.compiled.call(this, values, {});
60514         //var s = this.subs;
60515     },
60516
60517     apply : function(){
60518         return this.applyTemplate.apply(this, arguments);
60519     }
60520
60521  });
60522
60523 Roo.XTemplate.from = function(el){
60524     el = Roo.getDom(el);
60525     return new Roo.XTemplate(el.value || el.innerHTML);
60526 };