roojs-core.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         isTouch =  (function() {
67             try {  
68                 document.createEvent("TouchEvent");  
69                 return true;  
70             } catch (e) {  
71                 return false;  
72             } 
73             
74         })();
75     // remove css image flicker
76         if(isIE && !isIE7){
77         try{
78             document.execCommand("BackgroundImageCache", false, true);
79         }catch(e){}
80     }
81     
82     Roo.apply(Roo, {
83         /**
84          * True if the browser is in strict mode
85          * @type Boolean
86          */
87         isStrict : isStrict,
88         /**
89          * True if the page is running over SSL
90          * @type Boolean
91          */
92         isSecure : isSecure,
93         /**
94          * True when the document is fully initialized and ready for action
95          * @type Boolean
96          */
97         isReady : false,
98         /**
99          * Turn on debugging output (currently only the factory uses this)
100          * @type Boolean
101          */
102         
103         debug: false,
104
105         /**
106          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
107          * @type Boolean
108          */
109         enableGarbageCollector : true,
110
111         /**
112          * True to automatically purge event listeners after uncaching an element (defaults to false).
113          * Note: this only happens if enableGarbageCollector is true.
114          * @type Boolean
115          */
116         enableListenerCollection:false,
117
118         /**
119          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
120          * the IE insecure content warning (defaults to javascript:false).
121          * @type String
122          */
123         SSL_SECURE_URL : "javascript:false",
124
125         /**
126          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
127          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
128          * @type String
129          */
130         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
131
132         emptyFn : function(){},
133         
134         /**
135          * Copies all the properties of config to obj if they don't already exist.
136          * @param {Object} obj The receiver of the properties
137          * @param {Object} config The source of the properties
138          * @return {Object} returns obj
139          */
140         applyIf : function(o, c){
141             if(o && c){
142                 for(var p in c){
143                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
144                 }
145             }
146             return o;
147         },
148
149         /**
150          * Applies event listeners to elements by selectors when the document is ready.
151          * The event name is specified with an @ suffix.
152 <pre><code>
153 Roo.addBehaviors({
154    // add a listener for click on all anchors in element with id foo
155    '#foo a@click' : function(e, t){
156        // do something
157    },
158
159    // add the same listener to multiple selectors (separated by comma BEFORE the @)
160    '#foo a, #bar span.some-class@mouseover' : function(){
161        // do something
162    }
163 });
164 </code></pre>
165          * @param {Object} obj The list of behaviors to apply
166          */
167         addBehaviors : function(o){
168             if(!Roo.isReady){
169                 Roo.onReady(function(){
170                     Roo.addBehaviors(o);
171                 });
172                 return;
173             }
174             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
175             for(var b in o){
176                 var parts = b.split('@');
177                 if(parts[1]){ // for Object prototype breakers
178                     var s = parts[0];
179                     if(!cache[s]){
180                         cache[s] = Roo.select(s);
181                     }
182                     cache[s].on(parts[1], o[b]);
183                 }
184             }
185             cache = null;
186         },
187
188         /**
189          * Generates unique ids. If the element already has an id, it is unchanged
190          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
191          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
192          * @return {String} The generated Id.
193          */
194         id : function(el, prefix){
195             prefix = prefix || "roo-gen";
196             el = Roo.getDom(el);
197             var id = prefix + (++idSeed);
198             return el ? (el.id ? el.id : (el.id = id)) : id;
199         },
200          
201        
202         /**
203          * Extends one class with another class and optionally overrides members with the passed literal. This class
204          * also adds the function "override()" to the class that can be used to override
205          * members on an instance.
206          * @param {Object} subclass The class inheriting the functionality
207          * @param {Object} superclass The class being extended
208          * @param {Object} overrides (optional) A literal with members
209          * @method extend
210          */
211         extend : function(){
212             // inline overrides
213             var io = function(o){
214                 for(var m in o){
215                     this[m] = o[m];
216                 }
217             };
218             return function(sb, sp, overrides){
219                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
220                     overrides = sp;
221                     sp = sb;
222                     sb = function(){sp.apply(this, arguments);};
223                 }
224                 var F = function(){}, sbp, spp = sp.prototype;
225                 F.prototype = spp;
226                 sbp = sb.prototype = new F();
227                 sbp.constructor=sb;
228                 sb.superclass=spp;
229                 
230                 if(spp.constructor == Object.prototype.constructor){
231                     spp.constructor=sp;
232                    
233                 }
234                 
235                 sb.override = function(o){
236                     Roo.override(sb, o);
237                 };
238                 sbp.override = io;
239                 Roo.override(sb, overrides);
240                 return sb;
241             };
242         }(),
243
244         /**
245          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
246          * Usage:<pre><code>
247 Roo.override(MyClass, {
248     newMethod1: function(){
249         // etc.
250     },
251     newMethod2: function(foo){
252         // etc.
253     }
254 });
255  </code></pre>
256          * @param {Object} origclass The class to override
257          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
258          * containing one or more methods.
259          * @method override
260          */
261         override : function(origclass, overrides){
262             if(overrides){
263                 var p = origclass.prototype;
264                 for(var method in overrides){
265                     p[method] = overrides[method];
266                 }
267             }
268         },
269         /**
270          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
271          * <pre><code>
272 Roo.namespace('Company', 'Company.data');
273 Company.Widget = function() { ... }
274 Company.data.CustomStore = function(config) { ... }
275 </code></pre>
276          * @param {String} namespace1
277          * @param {String} namespace2
278          * @param {String} etc
279          * @method namespace
280          */
281         namespace : function(){
282             var a=arguments, o=null, i, j, d, rt;
283             for (i=0; i<a.length; ++i) {
284                 d=a[i].split(".");
285                 rt = d[0];
286                 /** eval:var:o */
287                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
288                 for (j=1; j<d.length; ++j) {
289                     o[d[j]]=o[d[j]] || {};
290                     o=o[d[j]];
291                 }
292             }
293         },
294         /**
295          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
296          * <pre><code>
297 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
298 Roo.factory(conf, Roo.data);
299 </code></pre>
300          * @param {String} classname
301          * @param {String} namespace (optional)
302          * @method factory
303          */
304          
305         factory : function(c, ns)
306         {
307             // no xtype, no ns or c.xns - or forced off by c.xns
308             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
309                 return c;
310             }
311             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
312             if (c.constructor == ns[c.xtype]) {// already created...
313                 return c;
314             }
315             if (ns[c.xtype]) {
316                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
317                 var ret = new ns[c.xtype](c);
318                 ret.xns = false;
319                 return ret;
320             }
321             c.xns = false; // prevent recursion..
322             return c;
323         },
324          /**
325          * Logs to console if it can.
326          *
327          * @param {String|Object} string
328          * @method log
329          */
330         log : function(s)
331         {
332             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
333                 return; // alerT?
334             }
335             console.log(s);
336             
337         },
338         /**
339          * 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.
340          * @param {Object} o
341          * @return {String}
342          */
343         urlEncode : function(o){
344             if(!o){
345                 return "";
346             }
347             var buf = [];
348             for(var key in o){
349                 var ov = o[key], k = Roo.encodeURIComponent(key);
350                 var type = typeof ov;
351                 if(type == 'undefined'){
352                     buf.push(k, "=&");
353                 }else if(type != "function" && type != "object"){
354                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
355                 }else if(ov instanceof Array){
356                     if (ov.length) {
357                             for(var i = 0, len = ov.length; i < len; i++) {
358                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
359                             }
360                         } else {
361                             buf.push(k, "=&");
362                         }
363                 }
364             }
365             buf.pop();
366             return buf.join("");
367         },
368          /**
369          * Safe version of encodeURIComponent
370          * @param {String} data 
371          * @return {String} 
372          */
373         
374         encodeURIComponent : function (data)
375         {
376             try {
377                 return encodeURIComponent(data);
378             } catch(e) {} // should be an uri encode error.
379             
380             if (data == '' || data == null){
381                return '';
382             }
383             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
384             function nibble_to_hex(nibble){
385                 var chars = '0123456789ABCDEF';
386                 return chars.charAt(nibble);
387             }
388             data = data.toString();
389             var buffer = '';
390             for(var i=0; i<data.length; i++){
391                 var c = data.charCodeAt(i);
392                 var bs = new Array();
393                 if (c > 0x10000){
394                         // 4 bytes
395                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
396                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
397                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
398                     bs[3] = 0x80 | (c & 0x3F);
399                 }else if (c > 0x800){
400                          // 3 bytes
401                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
402                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
403                     bs[2] = 0x80 | (c & 0x3F);
404                 }else if (c > 0x80){
405                        // 2 bytes
406                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
407                     bs[1] = 0x80 | (c & 0x3F);
408                 }else{
409                         // 1 byte
410                     bs[0] = c;
411                 }
412                 for(var j=0; j<bs.length; j++){
413                     var b = bs[j];
414                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
415                             + nibble_to_hex(b &0x0F);
416                     buffer += '%'+hex;
417                }
418             }
419             return buffer;    
420              
421         },
422
423         /**
424          * 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]}.
425          * @param {String} string
426          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
427          * @return {Object} A literal with members
428          */
429         urlDecode : function(string, overwrite){
430             if(!string || !string.length){
431                 return {};
432             }
433             var obj = {};
434             var pairs = string.split('&');
435             var pair, name, value;
436             for(var i = 0, len = pairs.length; i < len; i++){
437                 pair = pairs[i].split('=');
438                 name = decodeURIComponent(pair[0]);
439                 value = decodeURIComponent(pair[1]);
440                 if(overwrite !== true){
441                     if(typeof obj[name] == "undefined"){
442                         obj[name] = value;
443                     }else if(typeof obj[name] == "string"){
444                         obj[name] = [obj[name]];
445                         obj[name].push(value);
446                     }else{
447                         obj[name].push(value);
448                     }
449                 }else{
450                     obj[name] = value;
451                 }
452             }
453             return obj;
454         },
455
456         /**
457          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
458          * passed array is not really an array, your function is called once with it.
459          * The supplied function is called with (Object item, Number index, Array allItems).
460          * @param {Array/NodeList/Mixed} array
461          * @param {Function} fn
462          * @param {Object} scope
463          */
464         each : function(array, fn, scope){
465             if(typeof array.length == "undefined" || typeof array == "string"){
466                 array = [array];
467             }
468             for(var i = 0, len = array.length; i < len; i++){
469                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
470             }
471         },
472
473         // deprecated
474         combine : function(){
475             var as = arguments, l = as.length, r = [];
476             for(var i = 0; i < l; i++){
477                 var a = as[i];
478                 if(a instanceof Array){
479                     r = r.concat(a);
480                 }else if(a.length !== undefined && !a.substr){
481                     r = r.concat(Array.prototype.slice.call(a, 0));
482                 }else{
483                     r.push(a);
484                 }
485             }
486             return r;
487         },
488
489         /**
490          * Escapes the passed string for use in a regular expression
491          * @param {String} str
492          * @return {String}
493          */
494         escapeRe : function(s) {
495             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
496         },
497
498         // internal
499         callback : function(cb, scope, args, delay){
500             if(typeof cb == "function"){
501                 if(delay){
502                     cb.defer(delay, scope, args || []);
503                 }else{
504                     cb.apply(scope, args || []);
505                 }
506             }
507         },
508
509         /**
510          * Return the dom node for the passed string (id), dom node, or Roo.Element
511          * @param {String/HTMLElement/Roo.Element} el
512          * @return HTMLElement
513          */
514         getDom : function(el){
515             if(!el){
516                 return null;
517             }
518             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
519         },
520
521         /**
522         * Shorthand for {@link Roo.ComponentMgr#get}
523         * @param {String} id
524         * @return Roo.Component
525         */
526         getCmp : function(id){
527             return Roo.ComponentMgr.get(id);
528         },
529          
530         num : function(v, defaultValue){
531             if(typeof v != 'number'){
532                 return defaultValue;
533             }
534             return v;
535         },
536
537         destroy : function(){
538             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
539                 var as = a[i];
540                 if(as){
541                     if(as.dom){
542                         as.removeAllListeners();
543                         as.remove();
544                         continue;
545                     }
546                     if(typeof as.purgeListeners == 'function'){
547                         as.purgeListeners();
548                     }
549                     if(typeof as.destroy == 'function'){
550                         as.destroy();
551                     }
552                 }
553             }
554         },
555
556         // inpired by a similar function in mootools library
557         /**
558          * Returns the type of object that is passed in. If the object passed in is null or undefined it
559          * return false otherwise it returns one of the following values:<ul>
560          * <li><b>string</b>: If the object passed is a string</li>
561          * <li><b>number</b>: If the object passed is a number</li>
562          * <li><b>boolean</b>: If the object passed is a boolean value</li>
563          * <li><b>function</b>: If the object passed is a function reference</li>
564          * <li><b>object</b>: If the object passed is an object</li>
565          * <li><b>array</b>: If the object passed is an array</li>
566          * <li><b>regexp</b>: If the object passed is a regular expression</li>
567          * <li><b>element</b>: If the object passed is a DOM Element</li>
568          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
569          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
570          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
571          * @param {Mixed} object
572          * @return {String}
573          */
574         type : function(o){
575             if(o === undefined || o === null){
576                 return false;
577             }
578             if(o.htmlElement){
579                 return 'element';
580             }
581             var t = typeof o;
582             if(t == 'object' && o.nodeName) {
583                 switch(o.nodeType) {
584                     case 1: return 'element';
585                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
586                 }
587             }
588             if(t == 'object' || t == 'function') {
589                 switch(o.constructor) {
590                     case Array: return 'array';
591                     case RegExp: return 'regexp';
592                 }
593                 if(typeof o.length == 'number' && typeof o.item == 'function') {
594                     return 'nodelist';
595                 }
596             }
597             return t;
598         },
599
600         /**
601          * Returns true if the passed value is null, undefined or an empty string (optional).
602          * @param {Mixed} value The value to test
603          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
604          * @return {Boolean}
605          */
606         isEmpty : function(v, allowBlank){
607             return v === null || v === undefined || (!allowBlank ? v === '' : false);
608         },
609         
610         /** @type Boolean */
611         isOpera : isOpera,
612         /** @type Boolean */
613         isSafari : isSafari,
614         /** @type Boolean */
615         isFirefox : isFirefox,
616         /** @type Boolean */
617         isIE : isIE,
618         /** @type Boolean */
619         isIE7 : isIE7,
620         /** @type Boolean */
621         isIE11 : isIE11,
622         /** @type Boolean */
623         isGecko : isGecko,
624         /** @type Boolean */
625         isBorderBox : isBorderBox,
626         /** @type Boolean */
627         isWindows : isWindows,
628         /** @type Boolean */
629         isLinux : isLinux,
630         /** @type Boolean */
631         isMac : isMac,
632         /** @type Boolean */
633         isTouch : isTouch,
634
635         /**
636          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
637          * you may want to set this to true.
638          * @type Boolean
639          */
640         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
641         
642         
643                 
644         /**
645          * Selects a single element as a Roo Element
646          * This is about as close as you can get to jQuery's $('do crazy stuff')
647          * @param {String} selector The selector/xpath query
648          * @param {Node} root (optional) The start of the query (defaults to document).
649          * @return {Roo.Element}
650          */
651         selectNode : function(selector, root) 
652         {
653             var node = Roo.DomQuery.selectNode(selector,root);
654             return node ? Roo.get(node) : new Roo.Element(false);
655         }
656         
657     });
658
659
660 })();
661
662 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
663                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
664                 "Roo.app", "Roo.ux",
665                 "Roo.bootstrap",
666                 "Roo.bootstrap.dash");
667 /*
668  * Based on:
669  * Ext JS Library 1.1.1
670  * Copyright(c) 2006-2007, Ext JS, LLC.
671  *
672  * Originally Released Under LGPL - original licence link has changed is not relivant.
673  *
674  * Fork - LGPL
675  * <script type="text/javascript">
676  */
677
678 (function() {    
679     // wrappedn so fnCleanup is not in global scope...
680     if(Roo.isIE) {
681         function fnCleanUp() {
682             var p = Function.prototype;
683             delete p.createSequence;
684             delete p.defer;
685             delete p.createDelegate;
686             delete p.createCallback;
687             delete p.createInterceptor;
688
689             window.detachEvent("onunload", fnCleanUp);
690         }
691         window.attachEvent("onunload", fnCleanUp);
692     }
693 })();
694
695
696 /**
697  * @class Function
698  * These functions are available on every Function object (any JavaScript function).
699  */
700 Roo.apply(Function.prototype, {
701      /**
702      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
703      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
704      * Will create a function that is bound to those 2 args.
705      * @return {Function} The new function
706     */
707     createCallback : function(/*args...*/){
708         // make args available, in function below
709         var args = arguments;
710         var method = this;
711         return function() {
712             return method.apply(window, args);
713         };
714     },
715
716     /**
717      * Creates a delegate (callback) that sets the scope to obj.
718      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
719      * Will create a function that is automatically scoped to this.
720      * @param {Object} obj (optional) The object for which the scope is set
721      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
722      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
723      *                                             if a number the args are inserted at the specified position
724      * @return {Function} The new function
725      */
726     createDelegate : function(obj, args, appendArgs){
727         var method = this;
728         return function() {
729             var callArgs = args || arguments;
730             if(appendArgs === true){
731                 callArgs = Array.prototype.slice.call(arguments, 0);
732                 callArgs = callArgs.concat(args);
733             }else if(typeof appendArgs == "number"){
734                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
735                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
736                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
737             }
738             return method.apply(obj || window, callArgs);
739         };
740     },
741
742     /**
743      * Calls this function after the number of millseconds specified.
744      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
745      * @param {Object} obj (optional) The object for which the scope is set
746      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
747      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
748      *                                             if a number the args are inserted at the specified position
749      * @return {Number} The timeout id that can be used with clearTimeout
750      */
751     defer : function(millis, obj, args, appendArgs){
752         var fn = this.createDelegate(obj, args, appendArgs);
753         if(millis){
754             return setTimeout(fn, millis);
755         }
756         fn();
757         return 0;
758     },
759     /**
760      * Create a combined function call sequence of the original function + the passed function.
761      * The resulting function returns the results of the original function.
762      * The passed fcn is called with the parameters of the original function
763      * @param {Function} fcn The function to sequence
764      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
765      * @return {Function} The new function
766      */
767     createSequence : function(fcn, scope){
768         if(typeof fcn != "function"){
769             return this;
770         }
771         var method = this;
772         return function() {
773             var retval = method.apply(this || window, arguments);
774             fcn.apply(scope || this || window, arguments);
775             return retval;
776         };
777     },
778
779     /**
780      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
781      * The resulting function returns the results of the original function.
782      * The passed fcn is called with the parameters of the original function.
783      * @addon
784      * @param {Function} fcn The function to call before the original
785      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
786      * @return {Function} The new function
787      */
788     createInterceptor : function(fcn, scope){
789         if(typeof fcn != "function"){
790             return this;
791         }
792         var method = this;
793         return function() {
794             fcn.target = this;
795             fcn.method = method;
796             if(fcn.apply(scope || this || window, arguments) === false){
797                 return;
798             }
799             return method.apply(this || window, arguments);
800         };
801     }
802 });
803 /*
804  * Based on:
805  * Ext JS Library 1.1.1
806  * Copyright(c) 2006-2007, Ext JS, LLC.
807  *
808  * Originally Released Under LGPL - original licence link has changed is not relivant.
809  *
810  * Fork - LGPL
811  * <script type="text/javascript">
812  */
813
814 Roo.applyIf(String, {
815     
816     /** @scope String */
817     
818     /**
819      * Escapes the passed string for ' and \
820      * @param {String} string The string to escape
821      * @return {String} The escaped string
822      * @static
823      */
824     escape : function(string) {
825         return string.replace(/('|\\)/g, "\\$1");
826     },
827
828     /**
829      * Pads the left side of a string with a specified character.  This is especially useful
830      * for normalizing number and date strings.  Example usage:
831      * <pre><code>
832 var s = String.leftPad('123', 5, '0');
833 // s now contains the string: '00123'
834 </code></pre>
835      * @param {String} string The original string
836      * @param {Number} size The total length of the output string
837      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
838      * @return {String} The padded string
839      * @static
840      */
841     leftPad : function (val, size, ch) {
842         var result = new String(val);
843         if(ch === null || ch === undefined || ch === '') {
844             ch = " ";
845         }
846         while (result.length < size) {
847             result = ch + result;
848         }
849         return result;
850     },
851
852     /**
853      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
854      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
855      * <pre><code>
856 var cls = 'my-class', text = 'Some text';
857 var s = String.format('<div class="{0}">{1}</div>', cls, text);
858 // s now contains the string: '<div class="my-class">Some text</div>'
859 </code></pre>
860      * @param {String} string The tokenized string to be formatted
861      * @param {String} value1 The value to replace token {0}
862      * @param {String} value2 Etc...
863      * @return {String} The formatted string
864      * @static
865      */
866     format : function(format){
867         var args = Array.prototype.slice.call(arguments, 1);
868         return format.replace(/\{(\d+)\}/g, function(m, i){
869             return Roo.util.Format.htmlEncode(args[i]);
870         });
871     }
872 });
873
874 /**
875  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
876  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
877  * they are already different, the first value passed in is returned.  Note that this method returns the new value
878  * but does not change the current string.
879  * <pre><code>
880 // alternate sort directions
881 sort = sort.toggle('ASC', 'DESC');
882
883 // instead of conditional logic:
884 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
885 </code></pre>
886  * @param {String} value The value to compare to the current string
887  * @param {String} other The new value to use if the string already equals the first value passed in
888  * @return {String} The new value
889  */
890  
891 String.prototype.toggle = function(value, other){
892     return this == value ? other : value;
893 };/*
894  * Based on:
895  * Ext JS Library 1.1.1
896  * Copyright(c) 2006-2007, Ext JS, LLC.
897  *
898  * Originally Released Under LGPL - original licence link has changed is not relivant.
899  *
900  * Fork - LGPL
901  * <script type="text/javascript">
902  */
903
904  /**
905  * @class Number
906  */
907 Roo.applyIf(Number.prototype, {
908     /**
909      * Checks whether or not the current number is within a desired range.  If the number is already within the
910      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
911      * exceeded.  Note that this method returns the constrained value but does not change the current number.
912      * @param {Number} min The minimum number in the range
913      * @param {Number} max The maximum number in the range
914      * @return {Number} The constrained value if outside the range, otherwise the current value
915      */
916     constrain : function(min, max){
917         return Math.min(Math.max(this, min), max);
918     }
919 });/*
920  * Based on:
921  * Ext JS Library 1.1.1
922  * Copyright(c) 2006-2007, Ext JS, LLC.
923  *
924  * Originally Released Under LGPL - original licence link has changed is not relivant.
925  *
926  * Fork - LGPL
927  * <script type="text/javascript">
928  */
929  /**
930  * @class Array
931  */
932 Roo.applyIf(Array.prototype, {
933     /**
934      * 
935      * Checks whether or not the specified object exists in the array.
936      * @param {Object} o The object to check for
937      * @return {Number} The index of o in the array (or -1 if it is not found)
938      */
939     indexOf : function(o){
940        for (var i = 0, len = this.length; i < len; i++){
941               if(this[i] == o) return i;
942        }
943            return -1;
944     },
945
946     /**
947      * Removes the specified object from the array.  If the object is not found nothing happens.
948      * @param {Object} o The object to remove
949      */
950     remove : function(o){
951        var index = this.indexOf(o);
952        if(index != -1){
953            this.splice(index, 1);
954        }
955     },
956     /**
957      * Map (JS 1.6 compatibility)
958      * @param {Function} function  to call
959      */
960     map : function(fun )
961     {
962         var len = this.length >>> 0;
963         if (typeof fun != "function")
964             throw new TypeError();
965
966         var res = new Array(len);
967         var thisp = arguments[1];
968         for (var i = 0; i < len; i++)
969         {
970             if (i in this)
971                 res[i] = fun.call(thisp, this[i], i, this);
972         }
973
974         return res;
975     }
976     
977 });
978
979
980  /*
981  * Based on:
982  * Ext JS Library 1.1.1
983  * Copyright(c) 2006-2007, Ext JS, LLC.
984  *
985  * Originally Released Under LGPL - original licence link has changed is not relivant.
986  *
987  * Fork - LGPL
988  * <script type="text/javascript">
989  */
990
991 /**
992  * @class Date
993  *
994  * The date parsing and format syntax is a subset of
995  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
996  * supported will provide results equivalent to their PHP versions.
997  *
998  * Following is the list of all currently supported formats:
999  *<pre>
1000 Sample date:
1001 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1002
1003 Format  Output      Description
1004 ------  ----------  --------------------------------------------------------------
1005   d      10         Day of the month, 2 digits with leading zeros
1006   D      Wed        A textual representation of a day, three letters
1007   j      10         Day of the month without leading zeros
1008   l      Wednesday  A full textual representation of the day of the week
1009   S      th         English ordinal day of month suffix, 2 chars (use with j)
1010   w      3          Numeric representation of the day of the week
1011   z      9          The julian date, or day of the year (0-365)
1012   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1013   F      January    A full textual representation of the month
1014   m      01         Numeric representation of a month, with leading zeros
1015   M      Jan        Month name abbreviation, three letters
1016   n      1          Numeric representation of a month, without leading zeros
1017   t      31         Number of days in the given month
1018   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1019   Y      2007       A full numeric representation of a year, 4 digits
1020   y      07         A two digit representation of a year
1021   a      pm         Lowercase Ante meridiem and Post meridiem
1022   A      PM         Uppercase Ante meridiem and Post meridiem
1023   g      3          12-hour format of an hour without leading zeros
1024   G      15         24-hour format of an hour without leading zeros
1025   h      03         12-hour format of an hour with leading zeros
1026   H      15         24-hour format of an hour with leading zeros
1027   i      05         Minutes with leading zeros
1028   s      01         Seconds, with leading zeros
1029   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1030   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1031   T      CST        Timezone setting of the machine running the code
1032   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1033 </pre>
1034  *
1035  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1036  * <pre><code>
1037 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1038 document.write(dt.format('Y-m-d'));                         //2007-01-10
1039 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1040 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
1041  </code></pre>
1042  *
1043  * Here are some standard date/time patterns that you might find helpful.  They
1044  * are not part of the source of Date.js, but to use them you can simply copy this
1045  * block of code into any script that is included after Date.js and they will also become
1046  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1047  * <pre><code>
1048 Date.patterns = {
1049     ISO8601Long:"Y-m-d H:i:s",
1050     ISO8601Short:"Y-m-d",
1051     ShortDate: "n/j/Y",
1052     LongDate: "l, F d, Y",
1053     FullDateTime: "l, F d, Y g:i:s A",
1054     MonthDay: "F d",
1055     ShortTime: "g:i A",
1056     LongTime: "g:i:s A",
1057     SortableDateTime: "Y-m-d\\TH:i:s",
1058     UniversalSortableDateTime: "Y-m-d H:i:sO",
1059     YearMonth: "F, Y"
1060 };
1061 </code></pre>
1062  *
1063  * Example usage:
1064  * <pre><code>
1065 var dt = new Date();
1066 document.write(dt.format(Date.patterns.ShortDate));
1067  </code></pre>
1068  */
1069
1070 /*
1071  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1072  * They generate precompiled functions from date formats instead of parsing and
1073  * processing the pattern every time you format a date.  These functions are available
1074  * on every Date object (any javascript function).
1075  *
1076  * The original article and download are here:
1077  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1078  *
1079  */
1080  
1081  
1082  // was in core
1083 /**
1084  Returns the number of milliseconds between this date and date
1085  @param {Date} date (optional) Defaults to now
1086  @return {Number} The diff in milliseconds
1087  @member Date getElapsed
1088  */
1089 Date.prototype.getElapsed = function(date) {
1090         return Math.abs((date || new Date()).getTime()-this.getTime());
1091 };
1092 // was in date file..
1093
1094
1095 // private
1096 Date.parseFunctions = {count:0};
1097 // private
1098 Date.parseRegexes = [];
1099 // private
1100 Date.formatFunctions = {count:0};
1101
1102 // private
1103 Date.prototype.dateFormat = function(format) {
1104     if (Date.formatFunctions[format] == null) {
1105         Date.createNewFormat(format);
1106     }
1107     var func = Date.formatFunctions[format];
1108     return this[func]();
1109 };
1110
1111
1112 /**
1113  * Formats a date given the supplied format string
1114  * @param {String} format The format string
1115  * @return {String} The formatted date
1116  * @method
1117  */
1118 Date.prototype.format = Date.prototype.dateFormat;
1119
1120 // private
1121 Date.createNewFormat = function(format) {
1122     var funcName = "format" + Date.formatFunctions.count++;
1123     Date.formatFunctions[format] = funcName;
1124     var code = "Date.prototype." + funcName + " = function(){return ";
1125     var special = false;
1126     var ch = '';
1127     for (var i = 0; i < format.length; ++i) {
1128         ch = format.charAt(i);
1129         if (!special && ch == "\\") {
1130             special = true;
1131         }
1132         else if (special) {
1133             special = false;
1134             code += "'" + String.escape(ch) + "' + ";
1135         }
1136         else {
1137             code += Date.getFormatCode(ch);
1138         }
1139     }
1140     /** eval:var:zzzzzzzzzzzzz */
1141     eval(code.substring(0, code.length - 3) + ";}");
1142 };
1143
1144 // private
1145 Date.getFormatCode = function(character) {
1146     switch (character) {
1147     case "d":
1148         return "String.leftPad(this.getDate(), 2, '0') + ";
1149     case "D":
1150         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1151     case "j":
1152         return "this.getDate() + ";
1153     case "l":
1154         return "Date.dayNames[this.getDay()] + ";
1155     case "S":
1156         return "this.getSuffix() + ";
1157     case "w":
1158         return "this.getDay() + ";
1159     case "z":
1160         return "this.getDayOfYear() + ";
1161     case "W":
1162         return "this.getWeekOfYear() + ";
1163     case "F":
1164         return "Date.monthNames[this.getMonth()] + ";
1165     case "m":
1166         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1167     case "M":
1168         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1169     case "n":
1170         return "(this.getMonth() + 1) + ";
1171     case "t":
1172         return "this.getDaysInMonth() + ";
1173     case "L":
1174         return "(this.isLeapYear() ? 1 : 0) + ";
1175     case "Y":
1176         return "this.getFullYear() + ";
1177     case "y":
1178         return "('' + this.getFullYear()).substring(2, 4) + ";
1179     case "a":
1180         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1181     case "A":
1182         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1183     case "g":
1184         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1185     case "G":
1186         return "this.getHours() + ";
1187     case "h":
1188         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1189     case "H":
1190         return "String.leftPad(this.getHours(), 2, '0') + ";
1191     case "i":
1192         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1193     case "s":
1194         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1195     case "O":
1196         return "this.getGMTOffset() + ";
1197     case "P":
1198         return "this.getGMTColonOffset() + ";
1199     case "T":
1200         return "this.getTimezone() + ";
1201     case "Z":
1202         return "(this.getTimezoneOffset() * -60) + ";
1203     default:
1204         return "'" + String.escape(character) + "' + ";
1205     }
1206 };
1207
1208 /**
1209  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1210  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1211  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1212  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1213  * string or the parse operation will fail.
1214  * Example Usage:
1215 <pre><code>
1216 //dt = Fri May 25 2007 (current date)
1217 var dt = new Date();
1218
1219 //dt = Thu May 25 2006 (today's month/day in 2006)
1220 dt = Date.parseDate("2006", "Y");
1221
1222 //dt = Sun Jan 15 2006 (all date parts specified)
1223 dt = Date.parseDate("2006-1-15", "Y-m-d");
1224
1225 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1226 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1227 </code></pre>
1228  * @param {String} input The unparsed date as a string
1229  * @param {String} format The format the date is in
1230  * @return {Date} The parsed date
1231  * @static
1232  */
1233 Date.parseDate = function(input, format) {
1234     if (Date.parseFunctions[format] == null) {
1235         Date.createParser(format);
1236     }
1237     var func = Date.parseFunctions[format];
1238     return Date[func](input);
1239 };
1240 /**
1241  * @private
1242  */
1243
1244 Date.createParser = function(format) {
1245     var funcName = "parse" + Date.parseFunctions.count++;
1246     var regexNum = Date.parseRegexes.length;
1247     var currentGroup = 1;
1248     Date.parseFunctions[format] = funcName;
1249
1250     var code = "Date." + funcName + " = function(input){\n"
1251         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1252         + "var d = new Date();\n"
1253         + "y = d.getFullYear();\n"
1254         + "m = d.getMonth();\n"
1255         + "d = d.getDate();\n"
1256         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1257         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1258         + "if (results && results.length > 0) {";
1259     var regex = "";
1260
1261     var special = false;
1262     var ch = '';
1263     for (var i = 0; i < format.length; ++i) {
1264         ch = format.charAt(i);
1265         if (!special && ch == "\\") {
1266             special = true;
1267         }
1268         else if (special) {
1269             special = false;
1270             regex += String.escape(ch);
1271         }
1272         else {
1273             var obj = Date.formatCodeToRegex(ch, currentGroup);
1274             currentGroup += obj.g;
1275             regex += obj.s;
1276             if (obj.g && obj.c) {
1277                 code += obj.c;
1278             }
1279         }
1280     }
1281
1282     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1283         + "{v = new Date(y, m, d, h, i, s);}\n"
1284         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1285         + "{v = new Date(y, m, d, h, i);}\n"
1286         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1287         + "{v = new Date(y, m, d, h);}\n"
1288         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1289         + "{v = new Date(y, m, d);}\n"
1290         + "else if (y >= 0 && m >= 0)\n"
1291         + "{v = new Date(y, m);}\n"
1292         + "else if (y >= 0)\n"
1293         + "{v = new Date(y);}\n"
1294         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1295         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1296         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1297         + ";}";
1298
1299     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1300     /** eval:var:zzzzzzzzzzzzz */
1301     eval(code);
1302 };
1303
1304 // private
1305 Date.formatCodeToRegex = function(character, currentGroup) {
1306     switch (character) {
1307     case "D":
1308         return {g:0,
1309         c:null,
1310         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1311     case "j":
1312         return {g:1,
1313             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1314             s:"(\\d{1,2})"}; // day of month without leading zeroes
1315     case "d":
1316         return {g:1,
1317             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1318             s:"(\\d{2})"}; // day of month with leading zeroes
1319     case "l":
1320         return {g:0,
1321             c:null,
1322             s:"(?:" + Date.dayNames.join("|") + ")"};
1323     case "S":
1324         return {g:0,
1325             c:null,
1326             s:"(?:st|nd|rd|th)"};
1327     case "w":
1328         return {g:0,
1329             c:null,
1330             s:"\\d"};
1331     case "z":
1332         return {g:0,
1333             c:null,
1334             s:"(?:\\d{1,3})"};
1335     case "W":
1336         return {g:0,
1337             c:null,
1338             s:"(?:\\d{2})"};
1339     case "F":
1340         return {g:1,
1341             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1342             s:"(" + Date.monthNames.join("|") + ")"};
1343     case "M":
1344         return {g:1,
1345             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1346             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1347     case "n":
1348         return {g:1,
1349             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1350             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1351     case "m":
1352         return {g:1,
1353             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1354             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1355     case "t":
1356         return {g:0,
1357             c:null,
1358             s:"\\d{1,2}"};
1359     case "L":
1360         return {g:0,
1361             c:null,
1362             s:"(?:1|0)"};
1363     case "Y":
1364         return {g:1,
1365             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{4})"};
1367     case "y":
1368         return {g:1,
1369             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1370                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1371             s:"(\\d{1,2})"};
1372     case "a":
1373         return {g:1,
1374             c:"if (results[" + currentGroup + "] == 'am') {\n"
1375                 + "if (h == 12) { h = 0; }\n"
1376                 + "} else { if (h < 12) { h += 12; }}",
1377             s:"(am|pm)"};
1378     case "A":
1379         return {g:1,
1380             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1381                 + "if (h == 12) { h = 0; }\n"
1382                 + "} else { if (h < 12) { h += 12; }}",
1383             s:"(AM|PM)"};
1384     case "g":
1385     case "G":
1386         return {g:1,
1387             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1388             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1389     case "h":
1390     case "H":
1391         return {g:1,
1392             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1393             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1394     case "i":
1395         return {g:1,
1396             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1397             s:"(\\d{2})"};
1398     case "s":
1399         return {g:1,
1400             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1401             s:"(\\d{2})"};
1402     case "O":
1403         return {g:1,
1404             c:[
1405                 "o = results[", currentGroup, "];\n",
1406                 "var sn = o.substring(0,1);\n", // get + / - sign
1407                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1408                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1409                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1410                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1411             ].join(""),
1412             s:"([+\-]\\d{2,4})"};
1413     
1414     
1415     case "P":
1416         return {g:1,
1417                 c:[
1418                    "o = results[", currentGroup, "];\n",
1419                    "var sn = o.substring(0,1);\n",
1420                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1421                    "var mn = o.substring(4,6) % 60;\n",
1422                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1423                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1424             ].join(""),
1425             s:"([+\-]\\d{4})"};
1426     case "T":
1427         return {g:0,
1428             c:null,
1429             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1430     case "Z":
1431         return {g:1,
1432             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1433                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1434             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1435     default:
1436         return {g:0,
1437             c:null,
1438             s:String.escape(character)};
1439     }
1440 };
1441
1442 /**
1443  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1444  * @return {String} The abbreviated timezone name (e.g. 'CST')
1445  */
1446 Date.prototype.getTimezone = function() {
1447     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1448 };
1449
1450 /**
1451  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1452  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1453  */
1454 Date.prototype.getGMTOffset = function() {
1455     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1456         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1457         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1458 };
1459
1460 /**
1461  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1462  * @return {String} 2-characters representing hours and 2-characters representing minutes
1463  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1464  */
1465 Date.prototype.getGMTColonOffset = function() {
1466         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1467                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1468                 + ":"
1469                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1470 }
1471
1472 /**
1473  * Get the numeric day number of the year, adjusted for leap year.
1474  * @return {Number} 0 through 364 (365 in leap years)
1475  */
1476 Date.prototype.getDayOfYear = function() {
1477     var num = 0;
1478     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1479     for (var i = 0; i < this.getMonth(); ++i) {
1480         num += Date.daysInMonth[i];
1481     }
1482     return num + this.getDate() - 1;
1483 };
1484
1485 /**
1486  * Get the string representation of the numeric week number of the year
1487  * (equivalent to the format specifier 'W').
1488  * @return {String} '00' through '52'
1489  */
1490 Date.prototype.getWeekOfYear = function() {
1491     // Skip to Thursday of this week
1492     var now = this.getDayOfYear() + (4 - this.getDay());
1493     // Find the first Thursday of the year
1494     var jan1 = new Date(this.getFullYear(), 0, 1);
1495     var then = (7 - jan1.getDay() + 4);
1496     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1497 };
1498
1499 /**
1500  * Whether or not the current date is in a leap year.
1501  * @return {Boolean} True if the current date is in a leap year, else false
1502  */
1503 Date.prototype.isLeapYear = function() {
1504     var year = this.getFullYear();
1505     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1506 };
1507
1508 /**
1509  * Get the first day of the current month, adjusted for leap year.  The returned value
1510  * is the numeric day index within the week (0-6) which can be used in conjunction with
1511  * the {@link #monthNames} array to retrieve the textual day name.
1512  * Example:
1513  *<pre><code>
1514 var dt = new Date('1/10/2007');
1515 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1516 </code></pre>
1517  * @return {Number} The day number (0-6)
1518  */
1519 Date.prototype.getFirstDayOfMonth = function() {
1520     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1521     return (day < 0) ? (day + 7) : day;
1522 };
1523
1524 /**
1525  * Get the last day of the current month, adjusted for leap year.  The returned value
1526  * is the numeric day index within the week (0-6) which can be used in conjunction with
1527  * the {@link #monthNames} array to retrieve the textual day name.
1528  * Example:
1529  *<pre><code>
1530 var dt = new Date('1/10/2007');
1531 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1532 </code></pre>
1533  * @return {Number} The day number (0-6)
1534  */
1535 Date.prototype.getLastDayOfMonth = function() {
1536     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1537     return (day < 0) ? (day + 7) : day;
1538 };
1539
1540
1541 /**
1542  * Get the first date of this date's month
1543  * @return {Date}
1544  */
1545 Date.prototype.getFirstDateOfMonth = function() {
1546     return new Date(this.getFullYear(), this.getMonth(), 1);
1547 };
1548
1549 /**
1550  * Get the last date of this date's month
1551  * @return {Date}
1552  */
1553 Date.prototype.getLastDateOfMonth = function() {
1554     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1555 };
1556 /**
1557  * Get the number of days in the current month, adjusted for leap year.
1558  * @return {Number} The number of days in the month
1559  */
1560 Date.prototype.getDaysInMonth = function() {
1561     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1562     return Date.daysInMonth[this.getMonth()];
1563 };
1564
1565 /**
1566  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1567  * @return {String} 'st, 'nd', 'rd' or 'th'
1568  */
1569 Date.prototype.getSuffix = function() {
1570     switch (this.getDate()) {
1571         case 1:
1572         case 21:
1573         case 31:
1574             return "st";
1575         case 2:
1576         case 22:
1577             return "nd";
1578         case 3:
1579         case 23:
1580             return "rd";
1581         default:
1582             return "th";
1583     }
1584 };
1585
1586 // private
1587 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1588
1589 /**
1590  * An array of textual month names.
1591  * Override these values for international dates, for example...
1592  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1593  * @type Array
1594  * @static
1595  */
1596 Date.monthNames =
1597    ["January",
1598     "February",
1599     "March",
1600     "April",
1601     "May",
1602     "June",
1603     "July",
1604     "August",
1605     "September",
1606     "October",
1607     "November",
1608     "December"];
1609
1610 /**
1611  * An array of textual day names.
1612  * Override these values for international dates, for example...
1613  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1614  * @type Array
1615  * @static
1616  */
1617 Date.dayNames =
1618    ["Sunday",
1619     "Monday",
1620     "Tuesday",
1621     "Wednesday",
1622     "Thursday",
1623     "Friday",
1624     "Saturday"];
1625
1626 // private
1627 Date.y2kYear = 50;
1628 // private
1629 Date.monthNumbers = {
1630     Jan:0,
1631     Feb:1,
1632     Mar:2,
1633     Apr:3,
1634     May:4,
1635     Jun:5,
1636     Jul:6,
1637     Aug:7,
1638     Sep:8,
1639     Oct:9,
1640     Nov:10,
1641     Dec:11};
1642
1643 /**
1644  * Creates and returns a new Date instance with the exact same date value as the called instance.
1645  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1646  * variable will also be changed.  When the intention is to create a new variable that will not
1647  * modify the original instance, you should create a clone.
1648  *
1649  * Example of correctly cloning a date:
1650  * <pre><code>
1651 //wrong way:
1652 var orig = new Date('10/1/2006');
1653 var copy = orig;
1654 copy.setDate(5);
1655 document.write(orig);  //returns 'Thu Oct 05 2006'!
1656
1657 //correct way:
1658 var orig = new Date('10/1/2006');
1659 var copy = orig.clone();
1660 copy.setDate(5);
1661 document.write(orig);  //returns 'Thu Oct 01 2006'
1662 </code></pre>
1663  * @return {Date} The new Date instance
1664  */
1665 Date.prototype.clone = function() {
1666         return new Date(this.getTime());
1667 };
1668
1669 /**
1670  * Clears any time information from this date
1671  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1672  @return {Date} this or the clone
1673  */
1674 Date.prototype.clearTime = function(clone){
1675     if(clone){
1676         return this.clone().clearTime();
1677     }
1678     this.setHours(0);
1679     this.setMinutes(0);
1680     this.setSeconds(0);
1681     this.setMilliseconds(0);
1682     return this;
1683 };
1684
1685 // private
1686 // safari setMonth is broken
1687 if(Roo.isSafari){
1688     Date.brokenSetMonth = Date.prototype.setMonth;
1689         Date.prototype.setMonth = function(num){
1690                 if(num <= -1){
1691                         var n = Math.ceil(-num);
1692                         var back_year = Math.ceil(n/12);
1693                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1694                         this.setFullYear(this.getFullYear() - back_year);
1695                         return Date.brokenSetMonth.call(this, month);
1696                 } else {
1697                         return Date.brokenSetMonth.apply(this, arguments);
1698                 }
1699         };
1700 }
1701
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MILLI = "ms";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.SECOND = "s";
1710 /** Date interval constant 
1711 * @static 
1712 * @type String */
1713 Date.MINUTE = "mi";
1714 /** Date interval constant 
1715 * @static 
1716 * @type String */
1717 Date.HOUR = "h";
1718 /** Date interval constant 
1719 * @static 
1720 * @type String */
1721 Date.DAY = "d";
1722 /** Date interval constant 
1723 * @static 
1724 * @type String */
1725 Date.MONTH = "mo";
1726 /** Date interval constant 
1727 * @static 
1728 * @type String */
1729 Date.YEAR = "y";
1730
1731 /**
1732  * Provides a convenient method of performing basic date arithmetic.  This method
1733  * does not modify the Date instance being called - it creates and returns
1734  * a new Date instance containing the resulting date value.
1735  *
1736  * Examples:
1737  * <pre><code>
1738 //Basic usage:
1739 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1740 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1741
1742 //Negative values will subtract correctly:
1743 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1744 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1745
1746 //You can even chain several calls together in one line!
1747 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1748 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1749  </code></pre>
1750  *
1751  * @param {String} interval   A valid date interval enum value
1752  * @param {Number} value      The amount to add to the current date
1753  * @return {Date} The new Date instance
1754  */
1755 Date.prototype.add = function(interval, value){
1756   var d = this.clone();
1757   if (!interval || value === 0) return d;
1758   switch(interval.toLowerCase()){
1759     case Date.MILLI:
1760       d.setMilliseconds(this.getMilliseconds() + value);
1761       break;
1762     case Date.SECOND:
1763       d.setSeconds(this.getSeconds() + value);
1764       break;
1765     case Date.MINUTE:
1766       d.setMinutes(this.getMinutes() + value);
1767       break;
1768     case Date.HOUR:
1769       d.setHours(this.getHours() + value);
1770       break;
1771     case Date.DAY:
1772       d.setDate(this.getDate() + value);
1773       break;
1774     case Date.MONTH:
1775       var day = this.getDate();
1776       if(day > 28){
1777           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1778       }
1779       d.setDate(day);
1780       d.setMonth(this.getMonth() + value);
1781       break;
1782     case Date.YEAR:
1783       d.setFullYear(this.getFullYear() + value);
1784       break;
1785   }
1786   return d;
1787 };
1788 /*
1789  * Based on:
1790  * Ext JS Library 1.1.1
1791  * Copyright(c) 2006-2007, Ext JS, LLC.
1792  *
1793  * Originally Released Under LGPL - original licence link has changed is not relivant.
1794  *
1795  * Fork - LGPL
1796  * <script type="text/javascript">
1797  */
1798
1799 /**
1800  * @class Roo.lib.Dom
1801  * @static
1802  * 
1803  * Dom utils (from YIU afaik)
1804  * 
1805  **/
1806 Roo.lib.Dom = {
1807     /**
1808      * Get the view width
1809      * @param {Boolean} full True will get the full document, otherwise it's the view width
1810      * @return {Number} The width
1811      */
1812      
1813     getViewWidth : function(full) {
1814         return full ? this.getDocumentWidth() : this.getViewportWidth();
1815     },
1816     /**
1817      * Get the view height
1818      * @param {Boolean} full True will get the full document, otherwise it's the view height
1819      * @return {Number} The height
1820      */
1821     getViewHeight : function(full) {
1822         return full ? this.getDocumentHeight() : this.getViewportHeight();
1823     },
1824
1825     getDocumentHeight: function() {
1826         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1827         return Math.max(scrollHeight, this.getViewportHeight());
1828     },
1829
1830     getDocumentWidth: function() {
1831         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1832         return Math.max(scrollWidth, this.getViewportWidth());
1833     },
1834
1835     getViewportHeight: function() {
1836         var height = self.innerHeight;
1837         var mode = document.compatMode;
1838
1839         if ((mode || Roo.isIE) && !Roo.isOpera) {
1840             height = (mode == "CSS1Compat") ?
1841                      document.documentElement.clientHeight :
1842                      document.body.clientHeight;
1843         }
1844
1845         return height;
1846     },
1847
1848     getViewportWidth: function() {
1849         var width = self.innerWidth;
1850         var mode = document.compatMode;
1851
1852         if (mode || Roo.isIE) {
1853             width = (mode == "CSS1Compat") ?
1854                     document.documentElement.clientWidth :
1855                     document.body.clientWidth;
1856         }
1857         return width;
1858     },
1859
1860     isAncestor : function(p, c) {
1861         p = Roo.getDom(p);
1862         c = Roo.getDom(c);
1863         if (!p || !c) {
1864             return false;
1865         }
1866
1867         if (p.contains && !Roo.isSafari) {
1868             return p.contains(c);
1869         } else if (p.compareDocumentPosition) {
1870             return !!(p.compareDocumentPosition(c) & 16);
1871         } else {
1872             var parent = c.parentNode;
1873             while (parent) {
1874                 if (parent == p) {
1875                     return true;
1876                 }
1877                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1878                     return false;
1879                 }
1880                 parent = parent.parentNode;
1881             }
1882             return false;
1883         }
1884     },
1885
1886     getRegion : function(el) {
1887         return Roo.lib.Region.getRegion(el);
1888     },
1889
1890     getY : function(el) {
1891         return this.getXY(el)[1];
1892     },
1893
1894     getX : function(el) {
1895         return this.getXY(el)[0];
1896     },
1897
1898     getXY : function(el) {
1899         var p, pe, b, scroll, bd = document.body;
1900         el = Roo.getDom(el);
1901         var fly = Roo.lib.AnimBase.fly;
1902         if (el.getBoundingClientRect) {
1903             b = el.getBoundingClientRect();
1904             scroll = fly(document).getScroll();
1905             return [b.left + scroll.left, b.top + scroll.top];
1906         }
1907         var x = 0, y = 0;
1908
1909         p = el;
1910
1911         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1912
1913         while (p) {
1914
1915             x += p.offsetLeft;
1916             y += p.offsetTop;
1917
1918             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1919                 hasAbsolute = true;
1920             }
1921
1922             if (Roo.isGecko) {
1923                 pe = fly(p);
1924
1925                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1926                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1927
1928
1929                 x += bl;
1930                 y += bt;
1931
1932
1933                 if (p != el && pe.getStyle('overflow') != 'visible') {
1934                     x += bl;
1935                     y += bt;
1936                 }
1937             }
1938             p = p.offsetParent;
1939         }
1940
1941         if (Roo.isSafari && hasAbsolute) {
1942             x -= bd.offsetLeft;
1943             y -= bd.offsetTop;
1944         }
1945
1946         if (Roo.isGecko && !hasAbsolute) {
1947             var dbd = fly(bd);
1948             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1949             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1950         }
1951
1952         p = el.parentNode;
1953         while (p && p != bd) {
1954             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1955                 x -= p.scrollLeft;
1956                 y -= p.scrollTop;
1957             }
1958             p = p.parentNode;
1959         }
1960         return [x, y];
1961     },
1962  
1963   
1964
1965
1966     setXY : function(el, xy) {
1967         el = Roo.fly(el, '_setXY');
1968         el.position();
1969         var pts = el.translatePoints(xy);
1970         if (xy[0] !== false) {
1971             el.dom.style.left = pts.left + "px";
1972         }
1973         if (xy[1] !== false) {
1974             el.dom.style.top = pts.top + "px";
1975         }
1976     },
1977
1978     setX : function(el, x) {
1979         this.setXY(el, [x, false]);
1980     },
1981
1982     setY : function(el, y) {
1983         this.setXY(el, [false, y]);
1984     }
1985 };
1986 /*
1987  * Portions of this file are based on pieces of Yahoo User Interface Library
1988  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1989  * YUI licensed under the BSD License:
1990  * http://developer.yahoo.net/yui/license.txt
1991  * <script type="text/javascript">
1992  *
1993  */
1994
1995 Roo.lib.Event = function() {
1996     var loadComplete = false;
1997     var listeners = [];
1998     var unloadListeners = [];
1999     var retryCount = 0;
2000     var onAvailStack = [];
2001     var counter = 0;
2002     var lastError = null;
2003
2004     return {
2005         POLL_RETRYS: 200,
2006         POLL_INTERVAL: 20,
2007         EL: 0,
2008         TYPE: 1,
2009         FN: 2,
2010         WFN: 3,
2011         OBJ: 3,
2012         ADJ_SCOPE: 4,
2013         _interval: null,
2014
2015         startInterval: function() {
2016             if (!this._interval) {
2017                 var self = this;
2018                 var callback = function() {
2019                     self._tryPreloadAttach();
2020                 };
2021                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2022
2023             }
2024         },
2025
2026         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2027             onAvailStack.push({ id:         p_id,
2028                 fn:         p_fn,
2029                 obj:        p_obj,
2030                 override:   p_override,
2031                 checkReady: false    });
2032
2033             retryCount = this.POLL_RETRYS;
2034             this.startInterval();
2035         },
2036
2037
2038         addListener: function(el, eventName, fn) {
2039             el = Roo.getDom(el);
2040             if (!el || !fn) {
2041                 return false;
2042             }
2043
2044             if ("unload" == eventName) {
2045                 unloadListeners[unloadListeners.length] =
2046                 [el, eventName, fn];
2047                 return true;
2048             }
2049
2050             var wrappedFn = function(e) {
2051                 return fn(Roo.lib.Event.getEvent(e));
2052             };
2053
2054             var li = [el, eventName, fn, wrappedFn];
2055
2056             var index = listeners.length;
2057             listeners[index] = li;
2058
2059             this.doAdd(el, eventName, wrappedFn, false);
2060             return true;
2061
2062         },
2063
2064
2065         removeListener: function(el, eventName, fn) {
2066             var i, len;
2067
2068             el = Roo.getDom(el);
2069
2070             if(!fn) {
2071                 return this.purgeElement(el, false, eventName);
2072             }
2073
2074
2075             if ("unload" == eventName) {
2076
2077                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2078                     var li = unloadListeners[i];
2079                     if (li &&
2080                         li[0] == el &&
2081                         li[1] == eventName &&
2082                         li[2] == fn) {
2083                         unloadListeners.splice(i, 1);
2084                         return true;
2085                     }
2086                 }
2087
2088                 return false;
2089             }
2090
2091             var cacheItem = null;
2092
2093
2094             var index = arguments[3];
2095
2096             if ("undefined" == typeof index) {
2097                 index = this._getCacheIndex(el, eventName, fn);
2098             }
2099
2100             if (index >= 0) {
2101                 cacheItem = listeners[index];
2102             }
2103
2104             if (!el || !cacheItem) {
2105                 return false;
2106             }
2107
2108             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2109
2110             delete listeners[index][this.WFN];
2111             delete listeners[index][this.FN];
2112             listeners.splice(index, 1);
2113
2114             return true;
2115
2116         },
2117
2118
2119         getTarget: function(ev, resolveTextNode) {
2120             ev = ev.browserEvent || ev;
2121             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2122             var t = ev.target || ev.srcElement;
2123             return this.resolveTextNode(t);
2124         },
2125
2126
2127         resolveTextNode: function(node) {
2128             if (Roo.isSafari && node && 3 == node.nodeType) {
2129                 return node.parentNode;
2130             } else {
2131                 return node;
2132             }
2133         },
2134
2135
2136         getPageX: function(ev) {
2137             ev = ev.browserEvent || ev;
2138             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2139             var x = ev.pageX;
2140             if (!x && 0 !== x) {
2141                 x = ev.clientX || 0;
2142
2143                 if (Roo.isIE) {
2144                     x += this.getScroll()[1];
2145                 }
2146             }
2147
2148             return x;
2149         },
2150
2151
2152         getPageY: function(ev) {
2153             ev = ev.browserEvent || ev;
2154             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2155             var y = ev.pageY;
2156             if (!y && 0 !== y) {
2157                 y = ev.clientY || 0;
2158
2159                 if (Roo.isIE) {
2160                     y += this.getScroll()[0];
2161                 }
2162             }
2163
2164
2165             return y;
2166         },
2167
2168
2169         getXY: function(ev) {
2170             ev = ev.browserEvent || ev;
2171             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2172             return [this.getPageX(ev), this.getPageY(ev)];
2173         },
2174
2175
2176         getRelatedTarget: function(ev) {
2177             ev = ev.browserEvent || ev;
2178             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2179             var t = ev.relatedTarget;
2180             if (!t) {
2181                 if (ev.type == "mouseout") {
2182                     t = ev.toElement;
2183                 } else if (ev.type == "mouseover") {
2184                     t = ev.fromElement;
2185                 }
2186             }
2187
2188             return this.resolveTextNode(t);
2189         },
2190
2191
2192         getTime: function(ev) {
2193             ev = ev.browserEvent || ev;
2194             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2195             if (!ev.time) {
2196                 var t = new Date().getTime();
2197                 try {
2198                     ev.time = t;
2199                 } catch(ex) {
2200                     this.lastError = ex;
2201                     return t;
2202                 }
2203             }
2204
2205             return ev.time;
2206         },
2207
2208
2209         stopEvent: function(ev) {
2210             this.stopPropagation(ev);
2211             this.preventDefault(ev);
2212         },
2213
2214
2215         stopPropagation: function(ev) {
2216             ev = ev.browserEvent || ev;
2217             if (ev.stopPropagation) {
2218                 ev.stopPropagation();
2219             } else {
2220                 ev.cancelBubble = true;
2221             }
2222         },
2223
2224
2225         preventDefault: function(ev) {
2226             ev = ev.browserEvent || ev;
2227             if(ev.preventDefault) {
2228                 ev.preventDefault();
2229             } else {
2230                 ev.returnValue = false;
2231             }
2232         },
2233
2234
2235         getEvent: function(e) {
2236             var ev = e || window.event;
2237             if (!ev) {
2238                 var c = this.getEvent.caller;
2239                 while (c) {
2240                     ev = c.arguments[0];
2241                     if (ev && Event == ev.constructor) {
2242                         break;
2243                     }
2244                     c = c.caller;
2245                 }
2246             }
2247             return ev;
2248         },
2249
2250
2251         getCharCode: function(ev) {
2252             ev = ev.browserEvent || ev;
2253             return ev.charCode || ev.keyCode || 0;
2254         },
2255
2256
2257         _getCacheIndex: function(el, eventName, fn) {
2258             for (var i = 0,len = listeners.length; i < len; ++i) {
2259                 var li = listeners[i];
2260                 if (li &&
2261                     li[this.FN] == fn &&
2262                     li[this.EL] == el &&
2263                     li[this.TYPE] == eventName) {
2264                     return i;
2265                 }
2266             }
2267
2268             return -1;
2269         },
2270
2271
2272         elCache: {},
2273
2274
2275         getEl: function(id) {
2276             return document.getElementById(id);
2277         },
2278
2279
2280         clearCache: function() {
2281         },
2282
2283
2284         _load: function(e) {
2285             loadComplete = true;
2286             var EU = Roo.lib.Event;
2287
2288
2289             if (Roo.isIE) {
2290                 EU.doRemove(window, "load", EU._load);
2291             }
2292         },
2293
2294
2295         _tryPreloadAttach: function() {
2296
2297             if (this.locked) {
2298                 return false;
2299             }
2300
2301             this.locked = true;
2302
2303
2304             var tryAgain = !loadComplete;
2305             if (!tryAgain) {
2306                 tryAgain = (retryCount > 0);
2307             }
2308
2309
2310             var notAvail = [];
2311             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2312                 var item = onAvailStack[i];
2313                 if (item) {
2314                     var el = this.getEl(item.id);
2315
2316                     if (el) {
2317                         if (!item.checkReady ||
2318                             loadComplete ||
2319                             el.nextSibling ||
2320                             (document && document.body)) {
2321
2322                             var scope = el;
2323                             if (item.override) {
2324                                 if (item.override === true) {
2325                                     scope = item.obj;
2326                                 } else {
2327                                     scope = item.override;
2328                                 }
2329                             }
2330                             item.fn.call(scope, item.obj);
2331                             onAvailStack[i] = null;
2332                         }
2333                     } else {
2334                         notAvail.push(item);
2335                     }
2336                 }
2337             }
2338
2339             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2340
2341             if (tryAgain) {
2342
2343                 this.startInterval();
2344             } else {
2345                 clearInterval(this._interval);
2346                 this._interval = null;
2347             }
2348
2349             this.locked = false;
2350
2351             return true;
2352
2353         },
2354
2355
2356         purgeElement: function(el, recurse, eventName) {
2357             var elListeners = this.getListeners(el, eventName);
2358             if (elListeners) {
2359                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2360                     var l = elListeners[i];
2361                     this.removeListener(el, l.type, l.fn);
2362                 }
2363             }
2364
2365             if (recurse && el && el.childNodes) {
2366                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2367                     this.purgeElement(el.childNodes[i], recurse, eventName);
2368                 }
2369             }
2370         },
2371
2372
2373         getListeners: function(el, eventName) {
2374             var results = [], searchLists;
2375             if (!eventName) {
2376                 searchLists = [listeners, unloadListeners];
2377             } else if (eventName == "unload") {
2378                 searchLists = [unloadListeners];
2379             } else {
2380                 searchLists = [listeners];
2381             }
2382
2383             for (var j = 0; j < searchLists.length; ++j) {
2384                 var searchList = searchLists[j];
2385                 if (searchList && searchList.length > 0) {
2386                     for (var i = 0,len = searchList.length; i < len; ++i) {
2387                         var l = searchList[i];
2388                         if (l && l[this.EL] === el &&
2389                             (!eventName || eventName === l[this.TYPE])) {
2390                             results.push({
2391                                 type:   l[this.TYPE],
2392                                 fn:     l[this.FN],
2393                                 obj:    l[this.OBJ],
2394                                 adjust: l[this.ADJ_SCOPE],
2395                                 index:  i
2396                             });
2397                         }
2398                     }
2399                 }
2400             }
2401
2402             return (results.length) ? results : null;
2403         },
2404
2405
2406         _unload: function(e) {
2407
2408             var EU = Roo.lib.Event, i, j, l, len, index;
2409
2410             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2411                 l = unloadListeners[i];
2412                 if (l) {
2413                     var scope = window;
2414                     if (l[EU.ADJ_SCOPE]) {
2415                         if (l[EU.ADJ_SCOPE] === true) {
2416                             scope = l[EU.OBJ];
2417                         } else {
2418                             scope = l[EU.ADJ_SCOPE];
2419                         }
2420                     }
2421                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2422                     unloadListeners[i] = null;
2423                     l = null;
2424                     scope = null;
2425                 }
2426             }
2427
2428             unloadListeners = null;
2429
2430             if (listeners && listeners.length > 0) {
2431                 j = listeners.length;
2432                 while (j) {
2433                     index = j - 1;
2434                     l = listeners[index];
2435                     if (l) {
2436                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2437                                 l[EU.FN], index);
2438                     }
2439                     j = j - 1;
2440                 }
2441                 l = null;
2442
2443                 EU.clearCache();
2444             }
2445
2446             EU.doRemove(window, "unload", EU._unload);
2447
2448         },
2449
2450
2451         getScroll: function() {
2452             var dd = document.documentElement, db = document.body;
2453             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2454                 return [dd.scrollTop, dd.scrollLeft];
2455             } else if (db) {
2456                 return [db.scrollTop, db.scrollLeft];
2457             } else {
2458                 return [0, 0];
2459             }
2460         },
2461
2462
2463         doAdd: function () {
2464             if (window.addEventListener) {
2465                 return function(el, eventName, fn, capture) {
2466                     el.addEventListener(eventName, fn, (capture));
2467                 };
2468             } else if (window.attachEvent) {
2469                 return function(el, eventName, fn, capture) {
2470                     el.attachEvent("on" + eventName, fn);
2471                 };
2472             } else {
2473                 return function() {
2474                 };
2475             }
2476         }(),
2477
2478
2479         doRemove: function() {
2480             if (window.removeEventListener) {
2481                 return function (el, eventName, fn, capture) {
2482                     el.removeEventListener(eventName, fn, (capture));
2483                 };
2484             } else if (window.detachEvent) {
2485                 return function (el, eventName, fn) {
2486                     el.detachEvent("on" + eventName, fn);
2487                 };
2488             } else {
2489                 return function() {
2490                 };
2491             }
2492         }()
2493     };
2494     
2495 }();
2496 (function() {     
2497    
2498     var E = Roo.lib.Event;
2499     E.on = E.addListener;
2500     E.un = E.removeListener;
2501
2502     if (document && document.body) {
2503         E._load();
2504     } else {
2505         E.doAdd(window, "load", E._load);
2506     }
2507     E.doAdd(window, "unload", E._unload);
2508     E._tryPreloadAttach();
2509 })();
2510
2511 /*
2512  * Portions of this file are based on pieces of Yahoo User Interface Library
2513  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2514  * YUI licensed under the BSD License:
2515  * http://developer.yahoo.net/yui/license.txt
2516  * <script type="text/javascript">
2517  *
2518  */
2519
2520 (function() {
2521     /**
2522      * @class Roo.lib.Ajax
2523      *
2524      */
2525     Roo.lib.Ajax = {
2526         /**
2527          * @static 
2528          */
2529         request : function(method, uri, cb, data, options) {
2530             if(options){
2531                 var hs = options.headers;
2532                 if(hs){
2533                     for(var h in hs){
2534                         if(hs.hasOwnProperty(h)){
2535                             this.initHeader(h, hs[h], false);
2536                         }
2537                     }
2538                 }
2539                 if(options.xmlData){
2540                     this.initHeader('Content-Type', 'text/xml', false);
2541                     method = 'POST';
2542                     data = options.xmlData;
2543                 }
2544             }
2545
2546             return this.asyncRequest(method, uri, cb, data);
2547         },
2548
2549         serializeForm : function(form) {
2550             if(typeof form == 'string') {
2551                 form = (document.getElementById(form) || document.forms[form]);
2552             }
2553
2554             var el, name, val, disabled, data = '', hasSubmit = false;
2555             for (var i = 0; i < form.elements.length; i++) {
2556                 el = form.elements[i];
2557                 disabled = form.elements[i].disabled;
2558                 name = form.elements[i].name;
2559                 val = form.elements[i].value;
2560
2561                 if (!disabled && name){
2562                     switch (el.type)
2563                             {
2564                         case 'select-one':
2565                         case 'select-multiple':
2566                             for (var j = 0; j < el.options.length; j++) {
2567                                 if (el.options[j].selected) {
2568                                     if (Roo.isIE) {
2569                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2570                                     }
2571                                     else {
2572                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2573                                     }
2574                                 }
2575                             }
2576                             break;
2577                         case 'radio':
2578                         case 'checkbox':
2579                             if (el.checked) {
2580                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2581                             }
2582                             break;
2583                         case 'file':
2584
2585                         case undefined:
2586
2587                         case 'reset':
2588
2589                         case 'button':
2590
2591                             break;
2592                         case 'submit':
2593                             if(hasSubmit == false) {
2594                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2595                                 hasSubmit = true;
2596                             }
2597                             break;
2598                         default:
2599                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2600                             break;
2601                     }
2602                 }
2603             }
2604             data = data.substr(0, data.length - 1);
2605             return data;
2606         },
2607
2608         headers:{},
2609
2610         hasHeaders:false,
2611
2612         useDefaultHeader:true,
2613
2614         defaultPostHeader:'application/x-www-form-urlencoded',
2615
2616         useDefaultXhrHeader:true,
2617
2618         defaultXhrHeader:'XMLHttpRequest',
2619
2620         hasDefaultHeaders:true,
2621
2622         defaultHeaders:{},
2623
2624         poll:{},
2625
2626         timeout:{},
2627
2628         pollInterval:50,
2629
2630         transactionId:0,
2631
2632         setProgId:function(id)
2633         {
2634             this.activeX.unshift(id);
2635         },
2636
2637         setDefaultPostHeader:function(b)
2638         {
2639             this.useDefaultHeader = b;
2640         },
2641
2642         setDefaultXhrHeader:function(b)
2643         {
2644             this.useDefaultXhrHeader = b;
2645         },
2646
2647         setPollingInterval:function(i)
2648         {
2649             if (typeof i == 'number' && isFinite(i)) {
2650                 this.pollInterval = i;
2651             }
2652         },
2653
2654         createXhrObject:function(transactionId)
2655         {
2656             var obj,http;
2657             try
2658             {
2659
2660                 http = new XMLHttpRequest();
2661
2662                 obj = { conn:http, tId:transactionId };
2663             }
2664             catch(e)
2665             {
2666                 for (var i = 0; i < this.activeX.length; ++i) {
2667                     try
2668                     {
2669
2670                         http = new ActiveXObject(this.activeX[i]);
2671
2672                         obj = { conn:http, tId:transactionId };
2673                         break;
2674                     }
2675                     catch(e) {
2676                     }
2677                 }
2678             }
2679             finally
2680             {
2681                 return obj;
2682             }
2683         },
2684
2685         getConnectionObject:function()
2686         {
2687             var o;
2688             var tId = this.transactionId;
2689
2690             try
2691             {
2692                 o = this.createXhrObject(tId);
2693                 if (o) {
2694                     this.transactionId++;
2695                 }
2696             }
2697             catch(e) {
2698             }
2699             finally
2700             {
2701                 return o;
2702             }
2703         },
2704
2705         asyncRequest:function(method, uri, callback, postData)
2706         {
2707             var o = this.getConnectionObject();
2708
2709             if (!o) {
2710                 return null;
2711             }
2712             else {
2713                 o.conn.open(method, uri, true);
2714
2715                 if (this.useDefaultXhrHeader) {
2716                     if (!this.defaultHeaders['X-Requested-With']) {
2717                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2718                     }
2719                 }
2720
2721                 if(postData && this.useDefaultHeader){
2722                     this.initHeader('Content-Type', this.defaultPostHeader);
2723                 }
2724
2725                  if (this.hasDefaultHeaders || this.hasHeaders) {
2726                     this.setHeader(o);
2727                 }
2728
2729                 this.handleReadyState(o, callback);
2730                 o.conn.send(postData || null);
2731
2732                 return o;
2733             }
2734         },
2735
2736         handleReadyState:function(o, callback)
2737         {
2738             var oConn = this;
2739
2740             if (callback && callback.timeout) {
2741                 
2742                 this.timeout[o.tId] = window.setTimeout(function() {
2743                     oConn.abort(o, callback, true);
2744                 }, callback.timeout);
2745             }
2746
2747             this.poll[o.tId] = window.setInterval(
2748                     function() {
2749                         if (o.conn && o.conn.readyState == 4) {
2750                             window.clearInterval(oConn.poll[o.tId]);
2751                             delete oConn.poll[o.tId];
2752
2753                             if(callback && callback.timeout) {
2754                                 window.clearTimeout(oConn.timeout[o.tId]);
2755                                 delete oConn.timeout[o.tId];
2756                             }
2757
2758                             oConn.handleTransactionResponse(o, callback);
2759                         }
2760                     }
2761                     , this.pollInterval);
2762         },
2763
2764         handleTransactionResponse:function(o, callback, isAbort)
2765         {
2766
2767             if (!callback) {
2768                 this.releaseObject(o);
2769                 return;
2770             }
2771
2772             var httpStatus, responseObject;
2773
2774             try
2775             {
2776                 if (o.conn.status !== undefined && o.conn.status != 0) {
2777                     httpStatus = o.conn.status;
2778                 }
2779                 else {
2780                     httpStatus = 13030;
2781                 }
2782             }
2783             catch(e) {
2784
2785
2786                 httpStatus = 13030;
2787             }
2788
2789             if (httpStatus >= 200 && httpStatus < 300) {
2790                 responseObject = this.createResponseObject(o, callback.argument);
2791                 if (callback.success) {
2792                     if (!callback.scope) {
2793                         callback.success(responseObject);
2794                     }
2795                     else {
2796
2797
2798                         callback.success.apply(callback.scope, [responseObject]);
2799                     }
2800                 }
2801             }
2802             else {
2803                 switch (httpStatus) {
2804
2805                     case 12002:
2806                     case 12029:
2807                     case 12030:
2808                     case 12031:
2809                     case 12152:
2810                     case 13030:
2811                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2812                         if (callback.failure) {
2813                             if (!callback.scope) {
2814                                 callback.failure(responseObject);
2815                             }
2816                             else {
2817                                 callback.failure.apply(callback.scope, [responseObject]);
2818                             }
2819                         }
2820                         break;
2821                     default:
2822                         responseObject = this.createResponseObject(o, callback.argument);
2823                         if (callback.failure) {
2824                             if (!callback.scope) {
2825                                 callback.failure(responseObject);
2826                             }
2827                             else {
2828                                 callback.failure.apply(callback.scope, [responseObject]);
2829                             }
2830                         }
2831                 }
2832             }
2833
2834             this.releaseObject(o);
2835             responseObject = null;
2836         },
2837
2838         createResponseObject:function(o, callbackArg)
2839         {
2840             var obj = {};
2841             var headerObj = {};
2842
2843             try
2844             {
2845                 var headerStr = o.conn.getAllResponseHeaders();
2846                 var header = headerStr.split('\n');
2847                 for (var i = 0; i < header.length; i++) {
2848                     var delimitPos = header[i].indexOf(':');
2849                     if (delimitPos != -1) {
2850                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2851                     }
2852                 }
2853             }
2854             catch(e) {
2855             }
2856
2857             obj.tId = o.tId;
2858             obj.status = o.conn.status;
2859             obj.statusText = o.conn.statusText;
2860             obj.getResponseHeader = headerObj;
2861             obj.getAllResponseHeaders = headerStr;
2862             obj.responseText = o.conn.responseText;
2863             obj.responseXML = o.conn.responseXML;
2864
2865             if (typeof callbackArg !== undefined) {
2866                 obj.argument = callbackArg;
2867             }
2868
2869             return obj;
2870         },
2871
2872         createExceptionObject:function(tId, callbackArg, isAbort)
2873         {
2874             var COMM_CODE = 0;
2875             var COMM_ERROR = 'communication failure';
2876             var ABORT_CODE = -1;
2877             var ABORT_ERROR = 'transaction aborted';
2878
2879             var obj = {};
2880
2881             obj.tId = tId;
2882             if (isAbort) {
2883                 obj.status = ABORT_CODE;
2884                 obj.statusText = ABORT_ERROR;
2885             }
2886             else {
2887                 obj.status = COMM_CODE;
2888                 obj.statusText = COMM_ERROR;
2889             }
2890
2891             if (callbackArg) {
2892                 obj.argument = callbackArg;
2893             }
2894
2895             return obj;
2896         },
2897
2898         initHeader:function(label, value, isDefault)
2899         {
2900             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2901
2902             if (headerObj[label] === undefined) {
2903                 headerObj[label] = value;
2904             }
2905             else {
2906
2907
2908                 headerObj[label] = value + "," + headerObj[label];
2909             }
2910
2911             if (isDefault) {
2912                 this.hasDefaultHeaders = true;
2913             }
2914             else {
2915                 this.hasHeaders = true;
2916             }
2917         },
2918
2919
2920         setHeader:function(o)
2921         {
2922             if (this.hasDefaultHeaders) {
2923                 for (var prop in this.defaultHeaders) {
2924                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2925                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2926                     }
2927                 }
2928             }
2929
2930             if (this.hasHeaders) {
2931                 for (var prop in this.headers) {
2932                     if (this.headers.hasOwnProperty(prop)) {
2933                         o.conn.setRequestHeader(prop, this.headers[prop]);
2934                     }
2935                 }
2936                 this.headers = {};
2937                 this.hasHeaders = false;
2938             }
2939         },
2940
2941         resetDefaultHeaders:function() {
2942             delete this.defaultHeaders;
2943             this.defaultHeaders = {};
2944             this.hasDefaultHeaders = false;
2945         },
2946
2947         abort:function(o, callback, isTimeout)
2948         {
2949             if(this.isCallInProgress(o)) {
2950                 o.conn.abort();
2951                 window.clearInterval(this.poll[o.tId]);
2952                 delete this.poll[o.tId];
2953                 if (isTimeout) {
2954                     delete this.timeout[o.tId];
2955                 }
2956
2957                 this.handleTransactionResponse(o, callback, true);
2958
2959                 return true;
2960             }
2961             else {
2962                 return false;
2963             }
2964         },
2965
2966
2967         isCallInProgress:function(o)
2968         {
2969             if (o && o.conn) {
2970                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2971             }
2972             else {
2973
2974                 return false;
2975             }
2976         },
2977
2978
2979         releaseObject:function(o)
2980         {
2981
2982             o.conn = null;
2983
2984             o = null;
2985         },
2986
2987         activeX:[
2988         'MSXML2.XMLHTTP.3.0',
2989         'MSXML2.XMLHTTP',
2990         'Microsoft.XMLHTTP'
2991         ]
2992
2993
2994     };
2995 })();/*
2996  * Portions of this file are based on pieces of Yahoo User Interface Library
2997  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2998  * YUI licensed under the BSD License:
2999  * http://developer.yahoo.net/yui/license.txt
3000  * <script type="text/javascript">
3001  *
3002  */
3003
3004 Roo.lib.Region = function(t, r, b, l) {
3005     this.top = t;
3006     this[1] = t;
3007     this.right = r;
3008     this.bottom = b;
3009     this.left = l;
3010     this[0] = l;
3011 };
3012
3013
3014 Roo.lib.Region.prototype = {
3015     contains : function(region) {
3016         return ( region.left >= this.left &&
3017                  region.right <= this.right &&
3018                  region.top >= this.top &&
3019                  region.bottom <= this.bottom    );
3020
3021     },
3022
3023     getArea : function() {
3024         return ( (this.bottom - this.top) * (this.right - this.left) );
3025     },
3026
3027     intersect : function(region) {
3028         var t = Math.max(this.top, region.top);
3029         var r = Math.min(this.right, region.right);
3030         var b = Math.min(this.bottom, region.bottom);
3031         var l = Math.max(this.left, region.left);
3032
3033         if (b >= t && r >= l) {
3034             return new Roo.lib.Region(t, r, b, l);
3035         } else {
3036             return null;
3037         }
3038     },
3039     union : function(region) {
3040         var t = Math.min(this.top, region.top);
3041         var r = Math.max(this.right, region.right);
3042         var b = Math.max(this.bottom, region.bottom);
3043         var l = Math.min(this.left, region.left);
3044
3045         return new Roo.lib.Region(t, r, b, l);
3046     },
3047
3048     adjust : function(t, l, b, r) {
3049         this.top += t;
3050         this.left += l;
3051         this.right += r;
3052         this.bottom += b;
3053         return this;
3054     }
3055 };
3056
3057 Roo.lib.Region.getRegion = function(el) {
3058     var p = Roo.lib.Dom.getXY(el);
3059
3060     var t = p[1];
3061     var r = p[0] + el.offsetWidth;
3062     var b = p[1] + el.offsetHeight;
3063     var l = p[0];
3064
3065     return new Roo.lib.Region(t, r, b, l);
3066 };
3067 /*
3068  * Portions of this file are based on pieces of Yahoo User Interface Library
3069  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3070  * YUI licensed under the BSD License:
3071  * http://developer.yahoo.net/yui/license.txt
3072  * <script type="text/javascript">
3073  *
3074  */
3075 //@@dep Roo.lib.Region
3076
3077
3078 Roo.lib.Point = function(x, y) {
3079     if (x instanceof Array) {
3080         y = x[1];
3081         x = x[0];
3082     }
3083     this.x = this.right = this.left = this[0] = x;
3084     this.y = this.top = this.bottom = this[1] = y;
3085 };
3086
3087 Roo.lib.Point.prototype = new Roo.lib.Region();
3088 /*
3089  * Portions of this file are based on pieces of Yahoo User Interface Library
3090  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3091  * YUI licensed under the BSD License:
3092  * http://developer.yahoo.net/yui/license.txt
3093  * <script type="text/javascript">
3094  *
3095  */
3096  
3097 (function() {   
3098
3099     Roo.lib.Anim = {
3100         scroll : function(el, args, duration, easing, cb, scope) {
3101             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3102         },
3103
3104         motion : function(el, args, duration, easing, cb, scope) {
3105             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3106         },
3107
3108         color : function(el, args, duration, easing, cb, scope) {
3109             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3110         },
3111
3112         run : function(el, args, duration, easing, cb, scope, type) {
3113             type = type || Roo.lib.AnimBase;
3114             if (typeof easing == "string") {
3115                 easing = Roo.lib.Easing[easing];
3116             }
3117             var anim = new type(el, args, duration, easing);
3118             anim.animateX(function() {
3119                 Roo.callback(cb, scope);
3120             });
3121             return anim;
3122         }
3123     };
3124 })();/*
3125  * Portions of this file are based on pieces of Yahoo User Interface Library
3126  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3127  * YUI licensed under the BSD License:
3128  * http://developer.yahoo.net/yui/license.txt
3129  * <script type="text/javascript">
3130  *
3131  */
3132
3133 (function() {    
3134     var libFlyweight;
3135     
3136     function fly(el) {
3137         if (!libFlyweight) {
3138             libFlyweight = new Roo.Element.Flyweight();
3139         }
3140         libFlyweight.dom = el;
3141         return libFlyweight;
3142     }
3143
3144     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3145     
3146    
3147     
3148     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3149         if (el) {
3150             this.init(el, attributes, duration, method);
3151         }
3152     };
3153
3154     Roo.lib.AnimBase.fly = fly;
3155     
3156     
3157     
3158     Roo.lib.AnimBase.prototype = {
3159
3160         toString: function() {
3161             var el = this.getEl();
3162             var id = el.id || el.tagName;
3163             return ("Anim " + id);
3164         },
3165
3166         patterns: {
3167             noNegatives:        /width|height|opacity|padding/i,
3168             offsetAttribute:  /^((width|height)|(top|left))$/,
3169             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3170             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3171         },
3172
3173
3174         doMethod: function(attr, start, end) {
3175             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3176         },
3177
3178
3179         setAttribute: function(attr, val, unit) {
3180             if (this.patterns.noNegatives.test(attr)) {
3181                 val = (val > 0) ? val : 0;
3182             }
3183
3184             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3185         },
3186
3187
3188         getAttribute: function(attr) {
3189             var el = this.getEl();
3190             var val = fly(el).getStyle(attr);
3191
3192             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3193                 return parseFloat(val);
3194             }
3195
3196             var a = this.patterns.offsetAttribute.exec(attr) || [];
3197             var pos = !!( a[3] );
3198             var box = !!( a[2] );
3199
3200
3201             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3202                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3203             } else {
3204                 val = 0;
3205             }
3206
3207             return val;
3208         },
3209
3210
3211         getDefaultUnit: function(attr) {
3212             if (this.patterns.defaultUnit.test(attr)) {
3213                 return 'px';
3214             }
3215
3216             return '';
3217         },
3218
3219         animateX : function(callback, scope) {
3220             var f = function() {
3221                 this.onComplete.removeListener(f);
3222                 if (typeof callback == "function") {
3223                     callback.call(scope || this, this);
3224                 }
3225             };
3226             this.onComplete.addListener(f, this);
3227             this.animate();
3228         },
3229
3230
3231         setRuntimeAttribute: function(attr) {
3232             var start;
3233             var end;
3234             var attributes = this.attributes;
3235
3236             this.runtimeAttributes[attr] = {};
3237
3238             var isset = function(prop) {
3239                 return (typeof prop !== 'undefined');
3240             };
3241
3242             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3243                 return false;
3244             }
3245
3246             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3247
3248
3249             if (isset(attributes[attr]['to'])) {
3250                 end = attributes[attr]['to'];
3251             } else if (isset(attributes[attr]['by'])) {
3252                 if (start.constructor == Array) {
3253                     end = [];
3254                     for (var i = 0, len = start.length; i < len; ++i) {
3255                         end[i] = start[i] + attributes[attr]['by'][i];
3256                     }
3257                 } else {
3258                     end = start + attributes[attr]['by'];
3259                 }
3260             }
3261
3262             this.runtimeAttributes[attr].start = start;
3263             this.runtimeAttributes[attr].end = end;
3264
3265
3266             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3267         },
3268
3269
3270         init: function(el, attributes, duration, method) {
3271
3272             var isAnimated = false;
3273
3274
3275             var startTime = null;
3276
3277
3278             var actualFrames = 0;
3279
3280
3281             el = Roo.getDom(el);
3282
3283
3284             this.attributes = attributes || {};
3285
3286
3287             this.duration = duration || 1;
3288
3289
3290             this.method = method || Roo.lib.Easing.easeNone;
3291
3292
3293             this.useSeconds = true;
3294
3295
3296             this.currentFrame = 0;
3297
3298
3299             this.totalFrames = Roo.lib.AnimMgr.fps;
3300
3301
3302             this.getEl = function() {
3303                 return el;
3304             };
3305
3306
3307             this.isAnimated = function() {
3308                 return isAnimated;
3309             };
3310
3311
3312             this.getStartTime = function() {
3313                 return startTime;
3314             };
3315
3316             this.runtimeAttributes = {};
3317
3318
3319             this.animate = function() {
3320                 if (this.isAnimated()) {
3321                     return false;
3322                 }
3323
3324                 this.currentFrame = 0;
3325
3326                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3327
3328                 Roo.lib.AnimMgr.registerElement(this);
3329             };
3330
3331
3332             this.stop = function(finish) {
3333                 if (finish) {
3334                     this.currentFrame = this.totalFrames;
3335                     this._onTween.fire();
3336                 }
3337                 Roo.lib.AnimMgr.stop(this);
3338             };
3339
3340             var onStart = function() {
3341                 this.onStart.fire();
3342
3343                 this.runtimeAttributes = {};
3344                 for (var attr in this.attributes) {
3345                     this.setRuntimeAttribute(attr);
3346                 }
3347
3348                 isAnimated = true;
3349                 actualFrames = 0;
3350                 startTime = new Date();
3351             };
3352
3353
3354             var onTween = function() {
3355                 var data = {
3356                     duration: new Date() - this.getStartTime(),
3357                     currentFrame: this.currentFrame
3358                 };
3359
3360                 data.toString = function() {
3361                     return (
3362                             'duration: ' + data.duration +
3363                             ', currentFrame: ' + data.currentFrame
3364                             );
3365                 };
3366
3367                 this.onTween.fire(data);
3368
3369                 var runtimeAttributes = this.runtimeAttributes;
3370
3371                 for (var attr in runtimeAttributes) {
3372                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3373                 }
3374
3375                 actualFrames += 1;
3376             };
3377
3378             var onComplete = function() {
3379                 var actual_duration = (new Date() - startTime) / 1000 ;
3380
3381                 var data = {
3382                     duration: actual_duration,
3383                     frames: actualFrames,
3384                     fps: actualFrames / actual_duration
3385                 };
3386
3387                 data.toString = function() {
3388                     return (
3389                             'duration: ' + data.duration +
3390                             ', frames: ' + data.frames +
3391                             ', fps: ' + data.fps
3392                             );
3393                 };
3394
3395                 isAnimated = false;
3396                 actualFrames = 0;
3397                 this.onComplete.fire(data);
3398             };
3399
3400
3401             this._onStart = new Roo.util.Event(this);
3402             this.onStart = new Roo.util.Event(this);
3403             this.onTween = new Roo.util.Event(this);
3404             this._onTween = new Roo.util.Event(this);
3405             this.onComplete = new Roo.util.Event(this);
3406             this._onComplete = new Roo.util.Event(this);
3407             this._onStart.addListener(onStart);
3408             this._onTween.addListener(onTween);
3409             this._onComplete.addListener(onComplete);
3410         }
3411     };
3412 })();
3413 /*
3414  * Portions of this file are based on pieces of Yahoo User Interface Library
3415  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3416  * YUI licensed under the BSD License:
3417  * http://developer.yahoo.net/yui/license.txt
3418  * <script type="text/javascript">
3419  *
3420  */
3421
3422 Roo.lib.AnimMgr = new function() {
3423
3424     var thread = null;
3425
3426
3427     var queue = [];
3428
3429
3430     var tweenCount = 0;
3431
3432
3433     this.fps = 1000;
3434
3435
3436     this.delay = 1;
3437
3438
3439     this.registerElement = function(tween) {
3440         queue[queue.length] = tween;
3441         tweenCount += 1;
3442         tween._onStart.fire();
3443         this.start();
3444     };
3445
3446
3447     this.unRegister = function(tween, index) {
3448         tween._onComplete.fire();
3449         index = index || getIndex(tween);
3450         if (index != -1) {
3451             queue.splice(index, 1);
3452         }
3453
3454         tweenCount -= 1;
3455         if (tweenCount <= 0) {
3456             this.stop();
3457         }
3458     };
3459
3460
3461     this.start = function() {
3462         if (thread === null) {
3463             thread = setInterval(this.run, this.delay);
3464         }
3465     };
3466
3467
3468     this.stop = function(tween) {
3469         if (!tween) {
3470             clearInterval(thread);
3471
3472             for (var i = 0, len = queue.length; i < len; ++i) {
3473                 if (queue[0].isAnimated()) {
3474                     this.unRegister(queue[0], 0);
3475                 }
3476             }
3477
3478             queue = [];
3479             thread = null;
3480             tweenCount = 0;
3481         }
3482         else {
3483             this.unRegister(tween);
3484         }
3485     };
3486
3487
3488     this.run = function() {
3489         for (var i = 0, len = queue.length; i < len; ++i) {
3490             var tween = queue[i];
3491             if (!tween || !tween.isAnimated()) {
3492                 continue;
3493             }
3494
3495             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3496             {
3497                 tween.currentFrame += 1;
3498
3499                 if (tween.useSeconds) {
3500                     correctFrame(tween);
3501                 }
3502                 tween._onTween.fire();
3503             }
3504             else {
3505                 Roo.lib.AnimMgr.stop(tween, i);
3506             }
3507         }
3508     };
3509
3510     var getIndex = function(anim) {
3511         for (var i = 0, len = queue.length; i < len; ++i) {
3512             if (queue[i] == anim) {
3513                 return i;
3514             }
3515         }
3516         return -1;
3517     };
3518
3519
3520     var correctFrame = function(tween) {
3521         var frames = tween.totalFrames;
3522         var frame = tween.currentFrame;
3523         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3524         var elapsed = (new Date() - tween.getStartTime());
3525         var tweak = 0;
3526
3527         if (elapsed < tween.duration * 1000) {
3528             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3529         } else {
3530             tweak = frames - (frame + 1);
3531         }
3532         if (tweak > 0 && isFinite(tweak)) {
3533             if (tween.currentFrame + tweak >= frames) {
3534                 tweak = frames - (frame + 1);
3535             }
3536
3537             tween.currentFrame += tweak;
3538         }
3539     };
3540 };
3541
3542     /*
3543  * Portions of this file are based on pieces of Yahoo User Interface Library
3544  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3545  * YUI licensed under the BSD License:
3546  * http://developer.yahoo.net/yui/license.txt
3547  * <script type="text/javascript">
3548  *
3549  */
3550 Roo.lib.Bezier = new function() {
3551
3552         this.getPosition = function(points, t) {
3553             var n = points.length;
3554             var tmp = [];
3555
3556             for (var i = 0; i < n; ++i) {
3557                 tmp[i] = [points[i][0], points[i][1]];
3558             }
3559
3560             for (var j = 1; j < n; ++j) {
3561                 for (i = 0; i < n - j; ++i) {
3562                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3563                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3564                 }
3565             }
3566
3567             return [ tmp[0][0], tmp[0][1] ];
3568
3569         };
3570     };/*
3571  * Portions of this file are based on pieces of Yahoo User Interface Library
3572  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3573  * YUI licensed under the BSD License:
3574  * http://developer.yahoo.net/yui/license.txt
3575  * <script type="text/javascript">
3576  *
3577  */
3578 (function() {
3579
3580     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3581         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3582     };
3583
3584     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3585
3586     var fly = Roo.lib.AnimBase.fly;
3587     var Y = Roo.lib;
3588     var superclass = Y.ColorAnim.superclass;
3589     var proto = Y.ColorAnim.prototype;
3590
3591     proto.toString = function() {
3592         var el = this.getEl();
3593         var id = el.id || el.tagName;
3594         return ("ColorAnim " + id);
3595     };
3596
3597     proto.patterns.color = /color$/i;
3598     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3599     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3600     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3601     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3602
3603
3604     proto.parseColor = function(s) {
3605         if (s.length == 3) {
3606             return s;
3607         }
3608
3609         var c = this.patterns.hex.exec(s);
3610         if (c && c.length == 4) {
3611             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3612         }
3613
3614         c = this.patterns.rgb.exec(s);
3615         if (c && c.length == 4) {
3616             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3617         }
3618
3619         c = this.patterns.hex3.exec(s);
3620         if (c && c.length == 4) {
3621             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3622         }
3623
3624         return null;
3625     };
3626     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3627     proto.getAttribute = function(attr) {
3628         var el = this.getEl();
3629         if (this.patterns.color.test(attr)) {
3630             var val = fly(el).getStyle(attr);
3631
3632             if (this.patterns.transparent.test(val)) {
3633                 var parent = el.parentNode;
3634                 val = fly(parent).getStyle(attr);
3635
3636                 while (parent && this.patterns.transparent.test(val)) {
3637                     parent = parent.parentNode;
3638                     val = fly(parent).getStyle(attr);
3639                     if (parent.tagName.toUpperCase() == 'HTML') {
3640                         val = '#fff';
3641                     }
3642                 }
3643             }
3644         } else {
3645             val = superclass.getAttribute.call(this, attr);
3646         }
3647
3648         return val;
3649     };
3650     proto.getAttribute = function(attr) {
3651         var el = this.getEl();
3652         if (this.patterns.color.test(attr)) {
3653             var val = fly(el).getStyle(attr);
3654
3655             if (this.patterns.transparent.test(val)) {
3656                 var parent = el.parentNode;
3657                 val = fly(parent).getStyle(attr);
3658
3659                 while (parent && this.patterns.transparent.test(val)) {
3660                     parent = parent.parentNode;
3661                     val = fly(parent).getStyle(attr);
3662                     if (parent.tagName.toUpperCase() == 'HTML') {
3663                         val = '#fff';
3664                     }
3665                 }
3666             }
3667         } else {
3668             val = superclass.getAttribute.call(this, attr);
3669         }
3670
3671         return val;
3672     };
3673
3674     proto.doMethod = function(attr, start, end) {
3675         var val;
3676
3677         if (this.patterns.color.test(attr)) {
3678             val = [];
3679             for (var i = 0, len = start.length; i < len; ++i) {
3680                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3681             }
3682
3683             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3684         }
3685         else {
3686             val = superclass.doMethod.call(this, attr, start, end);
3687         }
3688
3689         return val;
3690     };
3691
3692     proto.setRuntimeAttribute = function(attr) {
3693         superclass.setRuntimeAttribute.call(this, attr);
3694
3695         if (this.patterns.color.test(attr)) {
3696             var attributes = this.attributes;
3697             var start = this.parseColor(this.runtimeAttributes[attr].start);
3698             var end = this.parseColor(this.runtimeAttributes[attr].end);
3699
3700             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3701                 end = this.parseColor(attributes[attr].by);
3702
3703                 for (var i = 0, len = start.length; i < len; ++i) {
3704                     end[i] = start[i] + end[i];
3705                 }
3706             }
3707
3708             this.runtimeAttributes[attr].start = start;
3709             this.runtimeAttributes[attr].end = end;
3710         }
3711     };
3712 })();
3713
3714 /*
3715  * Portions of this file are based on pieces of Yahoo User Interface Library
3716  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3717  * YUI licensed under the BSD License:
3718  * http://developer.yahoo.net/yui/license.txt
3719  * <script type="text/javascript">
3720  *
3721  */
3722 Roo.lib.Easing = {
3723
3724
3725     easeNone: function (t, b, c, d) {
3726         return c * t / d + b;
3727     },
3728
3729
3730     easeIn: function (t, b, c, d) {
3731         return c * (t /= d) * t + b;
3732     },
3733
3734
3735     easeOut: function (t, b, c, d) {
3736         return -c * (t /= d) * (t - 2) + b;
3737     },
3738
3739
3740     easeBoth: function (t, b, c, d) {
3741         if ((t /= d / 2) < 1) {
3742             return c / 2 * t * t + b;
3743         }
3744
3745         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3746     },
3747
3748
3749     easeInStrong: function (t, b, c, d) {
3750         return c * (t /= d) * t * t * t + b;
3751     },
3752
3753
3754     easeOutStrong: function (t, b, c, d) {
3755         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3756     },
3757
3758
3759     easeBothStrong: function (t, b, c, d) {
3760         if ((t /= d / 2) < 1) {
3761             return c / 2 * t * t * t * t + b;
3762         }
3763
3764         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3765     },
3766
3767
3768
3769     elasticIn: function (t, b, c, d, a, p) {
3770         if (t == 0) {
3771             return b;
3772         }
3773         if ((t /= d) == 1) {
3774             return b + c;
3775         }
3776         if (!p) {
3777             p = d * .3;
3778         }
3779
3780         if (!a || a < Math.abs(c)) {
3781             a = c;
3782             var s = p / 4;
3783         }
3784         else {
3785             var s = p / (2 * Math.PI) * Math.asin(c / a);
3786         }
3787
3788         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789     },
3790
3791
3792     elasticOut: function (t, b, c, d, a, p) {
3793         if (t == 0) {
3794             return b;
3795         }
3796         if ((t /= d) == 1) {
3797             return b + c;
3798         }
3799         if (!p) {
3800             p = d * .3;
3801         }
3802
3803         if (!a || a < Math.abs(c)) {
3804             a = c;
3805             var s = p / 4;
3806         }
3807         else {
3808             var s = p / (2 * Math.PI) * Math.asin(c / a);
3809         }
3810
3811         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3812     },
3813
3814
3815     elasticBoth: function (t, b, c, d, a, p) {
3816         if (t == 0) {
3817             return b;
3818         }
3819
3820         if ((t /= d / 2) == 2) {
3821             return b + c;
3822         }
3823
3824         if (!p) {
3825             p = d * (.3 * 1.5);
3826         }
3827
3828         if (!a || a < Math.abs(c)) {
3829             a = c;
3830             var s = p / 4;
3831         }
3832         else {
3833             var s = p / (2 * Math.PI) * Math.asin(c / a);
3834         }
3835
3836         if (t < 1) {
3837             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3838                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3839         }
3840         return a * Math.pow(2, -10 * (t -= 1)) *
3841                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3842     },
3843
3844
3845
3846     backIn: function (t, b, c, d, s) {
3847         if (typeof s == 'undefined') {
3848             s = 1.70158;
3849         }
3850         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3851     },
3852
3853
3854     backOut: function (t, b, c, d, s) {
3855         if (typeof s == 'undefined') {
3856             s = 1.70158;
3857         }
3858         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3859     },
3860
3861
3862     backBoth: function (t, b, c, d, s) {
3863         if (typeof s == 'undefined') {
3864             s = 1.70158;
3865         }
3866
3867         if ((t /= d / 2 ) < 1) {
3868             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3869         }
3870         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3871     },
3872
3873
3874     bounceIn: function (t, b, c, d) {
3875         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3876     },
3877
3878
3879     bounceOut: function (t, b, c, d) {
3880         if ((t /= d) < (1 / 2.75)) {
3881             return c * (7.5625 * t * t) + b;
3882         } else if (t < (2 / 2.75)) {
3883             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3884         } else if (t < (2.5 / 2.75)) {
3885             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3886         }
3887         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3888     },
3889
3890
3891     bounceBoth: function (t, b, c, d) {
3892         if (t < d / 2) {
3893             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3894         }
3895         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3896     }
3897 };/*
3898  * Portions of this file are based on pieces of Yahoo User Interface Library
3899  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3900  * YUI licensed under the BSD License:
3901  * http://developer.yahoo.net/yui/license.txt
3902  * <script type="text/javascript">
3903  *
3904  */
3905     (function() {
3906         Roo.lib.Motion = function(el, attributes, duration, method) {
3907             if (el) {
3908                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3909             }
3910         };
3911
3912         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3913
3914
3915         var Y = Roo.lib;
3916         var superclass = Y.Motion.superclass;
3917         var proto = Y.Motion.prototype;
3918
3919         proto.toString = function() {
3920             var el = this.getEl();
3921             var id = el.id || el.tagName;
3922             return ("Motion " + id);
3923         };
3924
3925         proto.patterns.points = /^points$/i;
3926
3927         proto.setAttribute = function(attr, val, unit) {
3928             if (this.patterns.points.test(attr)) {
3929                 unit = unit || 'px';
3930                 superclass.setAttribute.call(this, 'left', val[0], unit);
3931                 superclass.setAttribute.call(this, 'top', val[1], unit);
3932             } else {
3933                 superclass.setAttribute.call(this, attr, val, unit);
3934             }
3935         };
3936
3937         proto.getAttribute = function(attr) {
3938             if (this.patterns.points.test(attr)) {
3939                 var val = [
3940                         superclass.getAttribute.call(this, 'left'),
3941                         superclass.getAttribute.call(this, 'top')
3942                         ];
3943             } else {
3944                 val = superclass.getAttribute.call(this, attr);
3945             }
3946
3947             return val;
3948         };
3949
3950         proto.doMethod = function(attr, start, end) {
3951             var val = null;
3952
3953             if (this.patterns.points.test(attr)) {
3954                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3955                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3956             } else {
3957                 val = superclass.doMethod.call(this, attr, start, end);
3958             }
3959             return val;
3960         };
3961
3962         proto.setRuntimeAttribute = function(attr) {
3963             if (this.patterns.points.test(attr)) {
3964                 var el = this.getEl();
3965                 var attributes = this.attributes;
3966                 var start;
3967                 var control = attributes['points']['control'] || [];
3968                 var end;
3969                 var i, len;
3970
3971                 if (control.length > 0 && !(control[0] instanceof Array)) {
3972                     control = [control];
3973                 } else {
3974                     var tmp = [];
3975                     for (i = 0,len = control.length; i < len; ++i) {
3976                         tmp[i] = control[i];
3977                     }
3978                     control = tmp;
3979                 }
3980
3981                 Roo.fly(el).position();
3982
3983                 if (isset(attributes['points']['from'])) {
3984                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3985                 }
3986                 else {
3987                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3988                 }
3989
3990                 start = this.getAttribute('points');
3991
3992
3993                 if (isset(attributes['points']['to'])) {
3994                     end = translateValues.call(this, attributes['points']['to'], start);
3995
3996                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3997                     for (i = 0,len = control.length; i < len; ++i) {
3998                         control[i] = translateValues.call(this, control[i], start);
3999                     }
4000
4001
4002                 } else if (isset(attributes['points']['by'])) {
4003                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4004
4005                     for (i = 0,len = control.length; i < len; ++i) {
4006                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4007                     }
4008                 }
4009
4010                 this.runtimeAttributes[attr] = [start];
4011
4012                 if (control.length > 0) {
4013                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4014                 }
4015
4016                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4017             }
4018             else {
4019                 superclass.setRuntimeAttribute.call(this, attr);
4020             }
4021         };
4022
4023         var translateValues = function(val, start) {
4024             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4025             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4026
4027             return val;
4028         };
4029
4030         var isset = function(prop) {
4031             return (typeof prop !== 'undefined');
4032         };
4033     })();
4034 /*
4035  * Portions of this file are based on pieces of Yahoo User Interface Library
4036  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4037  * YUI licensed under the BSD License:
4038  * http://developer.yahoo.net/yui/license.txt
4039  * <script type="text/javascript">
4040  *
4041  */
4042     (function() {
4043         Roo.lib.Scroll = function(el, attributes, duration, method) {
4044             if (el) {
4045                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4046             }
4047         };
4048
4049         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4050
4051
4052         var Y = Roo.lib;
4053         var superclass = Y.Scroll.superclass;
4054         var proto = Y.Scroll.prototype;
4055
4056         proto.toString = function() {
4057             var el = this.getEl();
4058             var id = el.id || el.tagName;
4059             return ("Scroll " + id);
4060         };
4061
4062         proto.doMethod = function(attr, start, end) {
4063             var val = null;
4064
4065             if (attr == 'scroll') {
4066                 val = [
4067                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4068                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4069                         ];
4070
4071             } else {
4072                 val = superclass.doMethod.call(this, attr, start, end);
4073             }
4074             return val;
4075         };
4076
4077         proto.getAttribute = function(attr) {
4078             var val = null;
4079             var el = this.getEl();
4080
4081             if (attr == 'scroll') {
4082                 val = [ el.scrollLeft, el.scrollTop ];
4083             } else {
4084                 val = superclass.getAttribute.call(this, attr);
4085             }
4086
4087             return val;
4088         };
4089
4090         proto.setAttribute = function(attr, val, unit) {
4091             var el = this.getEl();
4092
4093             if (attr == 'scroll') {
4094                 el.scrollLeft = val[0];
4095                 el.scrollTop = val[1];
4096             } else {
4097                 superclass.setAttribute.call(this, attr, val, unit);
4098             }
4099         };
4100     })();
4101 /*
4102  * Based on:
4103  * Ext JS Library 1.1.1
4104  * Copyright(c) 2006-2007, Ext JS, LLC.
4105  *
4106  * Originally Released Under LGPL - original licence link has changed is not relivant.
4107  *
4108  * Fork - LGPL
4109  * <script type="text/javascript">
4110  */
4111
4112
4113 // nasty IE9 hack - what a pile of crap that is..
4114
4115  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4116     Range.prototype.createContextualFragment = function (html) {
4117         var doc = window.document;
4118         var container = doc.createElement("div");
4119         container.innerHTML = html;
4120         var frag = doc.createDocumentFragment(), n;
4121         while ((n = container.firstChild)) {
4122             frag.appendChild(n);
4123         }
4124         return frag;
4125     };
4126 }
4127
4128 /**
4129  * @class Roo.DomHelper
4130  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4131  * 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>.
4132  * @singleton
4133  */
4134 Roo.DomHelper = function(){
4135     var tempTableEl = null;
4136     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4137     var tableRe = /^table|tbody|tr|td$/i;
4138     var xmlns = {};
4139     // build as innerHTML where available
4140     /** @ignore */
4141     var createHtml = function(o){
4142         if(typeof o == 'string'){
4143             return o;
4144         }
4145         var b = "";
4146         if(!o.tag){
4147             o.tag = "div";
4148         }
4149         b += "<" + o.tag;
4150         for(var attr in o){
4151             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4152             if(attr == "style"){
4153                 var s = o["style"];
4154                 if(typeof s == "function"){
4155                     s = s.call();
4156                 }
4157                 if(typeof s == "string"){
4158                     b += ' style="' + s + '"';
4159                 }else if(typeof s == "object"){
4160                     b += ' style="';
4161                     for(var key in s){
4162                         if(typeof s[key] != "function"){
4163                             b += key + ":" + s[key] + ";";
4164                         }
4165                     }
4166                     b += '"';
4167                 }
4168             }else{
4169                 if(attr == "cls"){
4170                     b += ' class="' + o["cls"] + '"';
4171                 }else if(attr == "htmlFor"){
4172                     b += ' for="' + o["htmlFor"] + '"';
4173                 }else{
4174                     b += " " + attr + '="' + o[attr] + '"';
4175                 }
4176             }
4177         }
4178         if(emptyTags.test(o.tag)){
4179             b += "/>";
4180         }else{
4181             b += ">";
4182             var cn = o.children || o.cn;
4183             if(cn){
4184                 //http://bugs.kde.org/show_bug.cgi?id=71506
4185                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4186                     for(var i = 0, len = cn.length; i < len; i++) {
4187                         b += createHtml(cn[i], b);
4188                     }
4189                 }else{
4190                     b += createHtml(cn, b);
4191                 }
4192             }
4193             if(o.html){
4194                 b += o.html;
4195             }
4196             b += "</" + o.tag + ">";
4197         }
4198         return b;
4199     };
4200
4201     // build as dom
4202     /** @ignore */
4203     var createDom = function(o, parentNode){
4204          
4205         // defininition craeted..
4206         var ns = false;
4207         if (o.ns && o.ns != 'html') {
4208                
4209             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4210                 xmlns[o.ns] = o.xmlns;
4211                 ns = o.xmlns;
4212             }
4213             if (typeof(xmlns[o.ns]) == 'undefined') {
4214                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4215             }
4216             ns = xmlns[o.ns];
4217         }
4218         
4219         
4220         if (typeof(o) == 'string') {
4221             return parentNode.appendChild(document.createTextNode(o));
4222         }
4223         o.tag = o.tag || div;
4224         if (o.ns && Roo.isIE) {
4225             ns = false;
4226             o.tag = o.ns + ':' + o.tag;
4227             
4228         }
4229         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4230         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4231         for(var attr in o){
4232             
4233             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4234                     attr == "style" || typeof o[attr] == "function") continue;
4235                     
4236             if(attr=="cls" && Roo.isIE){
4237                 el.className = o["cls"];
4238             }else{
4239                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4240                 else el[attr] = o[attr];
4241             }
4242         }
4243         Roo.DomHelper.applyStyles(el, o.style);
4244         var cn = o.children || o.cn;
4245         if(cn){
4246             //http://bugs.kde.org/show_bug.cgi?id=71506
4247              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4248                 for(var i = 0, len = cn.length; i < len; i++) {
4249                     createDom(cn[i], el);
4250                 }
4251             }else{
4252                 createDom(cn, el);
4253             }
4254         }
4255         if(o.html){
4256             el.innerHTML = o.html;
4257         }
4258         if(parentNode){
4259            parentNode.appendChild(el);
4260         }
4261         return el;
4262     };
4263
4264     var ieTable = function(depth, s, h, e){
4265         tempTableEl.innerHTML = [s, h, e].join('');
4266         var i = -1, el = tempTableEl;
4267         while(++i < depth){
4268             el = el.firstChild;
4269         }
4270         return el;
4271     };
4272
4273     // kill repeat to save bytes
4274     var ts = '<table>',
4275         te = '</table>',
4276         tbs = ts+'<tbody>',
4277         tbe = '</tbody>'+te,
4278         trs = tbs + '<tr>',
4279         tre = '</tr>'+tbe;
4280
4281     /**
4282      * @ignore
4283      * Nasty code for IE's broken table implementation
4284      */
4285     var insertIntoTable = function(tag, where, el, html){
4286         if(!tempTableEl){
4287             tempTableEl = document.createElement('div');
4288         }
4289         var node;
4290         var before = null;
4291         if(tag == 'td'){
4292             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4293                 return;
4294             }
4295             if(where == 'beforebegin'){
4296                 before = el;
4297                 el = el.parentNode;
4298             } else{
4299                 before = el.nextSibling;
4300                 el = el.parentNode;
4301             }
4302             node = ieTable(4, trs, html, tre);
4303         }
4304         else if(tag == 'tr'){
4305             if(where == 'beforebegin'){
4306                 before = el;
4307                 el = el.parentNode;
4308                 node = ieTable(3, tbs, html, tbe);
4309             } else if(where == 'afterend'){
4310                 before = el.nextSibling;
4311                 el = el.parentNode;
4312                 node = ieTable(3, tbs, html, tbe);
4313             } else{ // INTO a TR
4314                 if(where == 'afterbegin'){
4315                     before = el.firstChild;
4316                 }
4317                 node = ieTable(4, trs, html, tre);
4318             }
4319         } else if(tag == 'tbody'){
4320             if(where == 'beforebegin'){
4321                 before = el;
4322                 el = el.parentNode;
4323                 node = ieTable(2, ts, html, te);
4324             } else if(where == 'afterend'){
4325                 before = el.nextSibling;
4326                 el = el.parentNode;
4327                 node = ieTable(2, ts, html, te);
4328             } else{
4329                 if(where == 'afterbegin'){
4330                     before = el.firstChild;
4331                 }
4332                 node = ieTable(3, tbs, html, tbe);
4333             }
4334         } else{ // TABLE
4335             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4336                 return;
4337             }
4338             if(where == 'afterbegin'){
4339                 before = el.firstChild;
4340             }
4341             node = ieTable(2, ts, html, te);
4342         }
4343         el.insertBefore(node, before);
4344         return node;
4345     };
4346
4347     return {
4348     /** True to force the use of DOM instead of html fragments @type Boolean */
4349     useDom : false,
4350
4351     /**
4352      * Returns the markup for the passed Element(s) config
4353      * @param {Object} o The Dom object spec (and children)
4354      * @return {String}
4355      */
4356     markup : function(o){
4357         return createHtml(o);
4358     },
4359
4360     /**
4361      * Applies a style specification to an element
4362      * @param {String/HTMLElement} el The element to apply styles to
4363      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4364      * a function which returns such a specification.
4365      */
4366     applyStyles : function(el, styles){
4367         if(styles){
4368            el = Roo.fly(el);
4369            if(typeof styles == "string"){
4370                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4371                var matches;
4372                while ((matches = re.exec(styles)) != null){
4373                    el.setStyle(matches[1], matches[2]);
4374                }
4375            }else if (typeof styles == "object"){
4376                for (var style in styles){
4377                   el.setStyle(style, styles[style]);
4378                }
4379            }else if (typeof styles == "function"){
4380                 Roo.DomHelper.applyStyles(el, styles.call());
4381            }
4382         }
4383     },
4384
4385     /**
4386      * Inserts an HTML fragment into the Dom
4387      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4388      * @param {HTMLElement} el The context element
4389      * @param {String} html The HTML fragmenet
4390      * @return {HTMLElement} The new node
4391      */
4392     insertHtml : function(where, el, html){
4393         where = where.toLowerCase();
4394         if(el.insertAdjacentHTML){
4395             if(tableRe.test(el.tagName)){
4396                 var rs;
4397                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4398                     return rs;
4399                 }
4400             }
4401             switch(where){
4402                 case "beforebegin":
4403                     el.insertAdjacentHTML('BeforeBegin', html);
4404                     return el.previousSibling;
4405                 case "afterbegin":
4406                     el.insertAdjacentHTML('AfterBegin', html);
4407                     return el.firstChild;
4408                 case "beforeend":
4409                     el.insertAdjacentHTML('BeforeEnd', html);
4410                     return el.lastChild;
4411                 case "afterend":
4412                     el.insertAdjacentHTML('AfterEnd', html);
4413                     return el.nextSibling;
4414             }
4415             throw 'Illegal insertion point -> "' + where + '"';
4416         }
4417         var range = el.ownerDocument.createRange();
4418         var frag;
4419         switch(where){
4420              case "beforebegin":
4421                 range.setStartBefore(el);
4422                 frag = range.createContextualFragment(html);
4423                 el.parentNode.insertBefore(frag, el);
4424                 return el.previousSibling;
4425              case "afterbegin":
4426                 if(el.firstChild){
4427                     range.setStartBefore(el.firstChild);
4428                     frag = range.createContextualFragment(html);
4429                     el.insertBefore(frag, el.firstChild);
4430                     return el.firstChild;
4431                 }else{
4432                     el.innerHTML = html;
4433                     return el.firstChild;
4434                 }
4435             case "beforeend":
4436                 if(el.lastChild){
4437                     range.setStartAfter(el.lastChild);
4438                     frag = range.createContextualFragment(html);
4439                     el.appendChild(frag);
4440                     return el.lastChild;
4441                 }else{
4442                     el.innerHTML = html;
4443                     return el.lastChild;
4444                 }
4445             case "afterend":
4446                 range.setStartAfter(el);
4447                 frag = range.createContextualFragment(html);
4448                 el.parentNode.insertBefore(frag, el.nextSibling);
4449                 return el.nextSibling;
4450             }
4451             throw 'Illegal insertion point -> "' + where + '"';
4452     },
4453
4454     /**
4455      * Creates new Dom element(s) and inserts them before el
4456      * @param {String/HTMLElement/Element} el The context element
4457      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4458      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4459      * @return {HTMLElement/Roo.Element} The new node
4460      */
4461     insertBefore : function(el, o, returnElement){
4462         return this.doInsert(el, o, returnElement, "beforeBegin");
4463     },
4464
4465     /**
4466      * Creates new Dom element(s) and inserts them after el
4467      * @param {String/HTMLElement/Element} el The context element
4468      * @param {Object} o The Dom object spec (and children)
4469      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4470      * @return {HTMLElement/Roo.Element} The new node
4471      */
4472     insertAfter : function(el, o, returnElement){
4473         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4474     },
4475
4476     /**
4477      * Creates new Dom element(s) and inserts them as the first child of el
4478      * @param {String/HTMLElement/Element} el The context element
4479      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4480      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4481      * @return {HTMLElement/Roo.Element} The new node
4482      */
4483     insertFirst : function(el, o, returnElement){
4484         return this.doInsert(el, o, returnElement, "afterBegin");
4485     },
4486
4487     // private
4488     doInsert : function(el, o, returnElement, pos, sibling){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml(pos, el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and appends them to el
4503      * @param {String/HTMLElement/Element} el The context element
4504      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4505      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4506      * @return {HTMLElement/Roo.Element} The new node
4507      */
4508     append : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         var newNode;
4511         if(this.useDom || o.ns){
4512             newNode = createDom(o, null);
4513             el.appendChild(newNode);
4514         }else{
4515             var html = createHtml(o);
4516             newNode = this.insertHtml("beforeEnd", el, html);
4517         }
4518         return returnElement ? Roo.get(newNode, true) : newNode;
4519     },
4520
4521     /**
4522      * Creates new Dom element(s) and overwrites the contents of el with them
4523      * @param {String/HTMLElement/Element} el The context element
4524      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4525      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4526      * @return {HTMLElement/Roo.Element} The new node
4527      */
4528     overwrite : function(el, o, returnElement){
4529         el = Roo.getDom(el);
4530         if (o.ns) {
4531           
4532             while (el.childNodes.length) {
4533                 el.removeChild(el.firstChild);
4534             }
4535             createDom(o, el);
4536         } else {
4537             el.innerHTML = createHtml(o);   
4538         }
4539         
4540         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4541     },
4542
4543     /**
4544      * Creates a new Roo.DomHelper.Template from the Dom object spec
4545      * @param {Object} o The Dom object spec (and children)
4546      * @return {Roo.DomHelper.Template} The new template
4547      */
4548     createTemplate : function(o){
4549         var html = createHtml(o);
4550         return new Roo.Template(html);
4551     }
4552     };
4553 }();
4554 /*
4555  * Based on:
4556  * Ext JS Library 1.1.1
4557  * Copyright(c) 2006-2007, Ext JS, LLC.
4558  *
4559  * Originally Released Under LGPL - original licence link has changed is not relivant.
4560  *
4561  * Fork - LGPL
4562  * <script type="text/javascript">
4563  */
4564  
4565 /**
4566 * @class Roo.Template
4567 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4568 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4569 * Usage:
4570 <pre><code>
4571 var t = new Roo.Template({
4572     html :  '&lt;div name="{id}"&gt;' + 
4573         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4574         '&lt;/div&gt;',
4575     myformat: function (value, allValues) {
4576         return 'XX' + value;
4577     }
4578 });
4579 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4580 </code></pre>
4581 * For more information see this blog post with examples:
4582 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4583      - Create Elements using DOM, HTML fragments and Templates</a>. 
4584 * @constructor
4585 * @param {Object} cfg - Configuration object.
4586 */
4587 Roo.Template = function(cfg){
4588     // BC!
4589     if(cfg instanceof Array){
4590         cfg = cfg.join("");
4591     }else if(arguments.length > 1){
4592         cfg = Array.prototype.join.call(arguments, "");
4593     }
4594     
4595     
4596     if (typeof(cfg) == 'object') {
4597         Roo.apply(this,cfg)
4598     } else {
4599         // bc
4600         this.html = cfg;
4601     }
4602     if (this.url) {
4603         this.load();
4604     }
4605     
4606 };
4607 Roo.Template.prototype = {
4608     
4609     /**
4610      * @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..
4611      *                    it should be fixed so that template is observable...
4612      */
4613     url : false,
4614     /**
4615      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4616      */
4617     html : '',
4618     /**
4619      * Returns an HTML fragment of this template with the specified values applied.
4620      * @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'})
4621      * @return {String} The HTML fragment
4622      */
4623     applyTemplate : function(values){
4624         try {
4625            
4626             if(this.compiled){
4627                 return this.compiled(values);
4628             }
4629             var useF = this.disableFormats !== true;
4630             var fm = Roo.util.Format, tpl = this;
4631             var fn = function(m, name, format, args){
4632                 if(format && useF){
4633                     if(format.substr(0, 5) == "this."){
4634                         return tpl.call(format.substr(5), values[name], values);
4635                     }else{
4636                         if(args){
4637                             // quoted values are required for strings in compiled templates, 
4638                             // but for non compiled we need to strip them
4639                             // quoted reversed for jsmin
4640                             var re = /^\s*['"](.*)["']\s*$/;
4641                             args = args.split(',');
4642                             for(var i = 0, len = args.length; i < len; i++){
4643                                 args[i] = args[i].replace(re, "$1");
4644                             }
4645                             args = [values[name]].concat(args);
4646                         }else{
4647                             args = [values[name]];
4648                         }
4649                         return fm[format].apply(fm, args);
4650                     }
4651                 }else{
4652                     return values[name] !== undefined ? values[name] : "";
4653                 }
4654             };
4655             return this.html.replace(this.re, fn);
4656         } catch (e) {
4657             Roo.log(e);
4658             throw e;
4659         }
4660          
4661     },
4662     
4663     loading : false,
4664       
4665     load : function ()
4666     {
4667          
4668         if (this.loading) {
4669             return;
4670         }
4671         var _t = this;
4672         
4673         this.loading = true;
4674         this.compiled = false;
4675         
4676         var cx = new Roo.data.Connection();
4677         cx.request({
4678             url : this.url,
4679             method : 'GET',
4680             success : function (response) {
4681                 _t.loading = false;
4682                 _t.html = response.responseText;
4683                 _t.url = false;
4684                 _t.compile();
4685              },
4686             failure : function(response) {
4687                 Roo.log("Template failed to load from " + _t.url);
4688                 _t.loading = false;
4689             }
4690         });
4691     },
4692
4693     /**
4694      * Sets the HTML used as the template and optionally compiles it.
4695      * @param {String} html
4696      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4697      * @return {Roo.Template} this
4698      */
4699     set : function(html, compile){
4700         this.html = html;
4701         this.compiled = null;
4702         if(compile){
4703             this.compile();
4704         }
4705         return this;
4706     },
4707     
4708     /**
4709      * True to disable format functions (defaults to false)
4710      * @type Boolean
4711      */
4712     disableFormats : false,
4713     
4714     /**
4715     * The regular expression used to match template variables 
4716     * @type RegExp
4717     * @property 
4718     */
4719     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4720     
4721     /**
4722      * Compiles the template into an internal function, eliminating the RegEx overhead.
4723      * @return {Roo.Template} this
4724      */
4725     compile : function(){
4726         var fm = Roo.util.Format;
4727         var useF = this.disableFormats !== true;
4728         var sep = Roo.isGecko ? "+" : ",";
4729         var fn = function(m, name, format, args){
4730             if(format && useF){
4731                 args = args ? ',' + args : "";
4732                 if(format.substr(0, 5) != "this."){
4733                     format = "fm." + format + '(';
4734                 }else{
4735                     format = 'this.call("'+ format.substr(5) + '", ';
4736                     args = ", values";
4737                 }
4738             }else{
4739                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4740             }
4741             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4742         };
4743         var body;
4744         // branched to use + in gecko and [].join() in others
4745         if(Roo.isGecko){
4746             body = "this.compiled = function(values){ return '" +
4747                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4748                     "';};";
4749         }else{
4750             body = ["this.compiled = function(values){ return ['"];
4751             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4752             body.push("'].join('');};");
4753             body = body.join('');
4754         }
4755         /**
4756          * eval:var:values
4757          * eval:var:fm
4758          */
4759         eval(body);
4760         return this;
4761     },
4762     
4763     // private function used to call members
4764     call : function(fnName, value, allValues){
4765         return this[fnName](value, allValues);
4766     },
4767     
4768     /**
4769      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4770      * @param {String/HTMLElement/Roo.Element} el The context element
4771      * @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'})
4772      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4773      * @return {HTMLElement/Roo.Element} The new node or Element
4774      */
4775     insertFirst: function(el, values, returnElement){
4776         return this.doInsert('afterBegin', el, values, returnElement);
4777     },
4778
4779     /**
4780      * Applies the supplied values to the template and inserts the new node(s) before el.
4781      * @param {String/HTMLElement/Roo.Element} el The context element
4782      * @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'})
4783      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4784      * @return {HTMLElement/Roo.Element} The new node or Element
4785      */
4786     insertBefore: function(el, values, returnElement){
4787         return this.doInsert('beforeBegin', el, values, returnElement);
4788     },
4789
4790     /**
4791      * Applies the supplied values to the template and inserts the new node(s) after el.
4792      * @param {String/HTMLElement/Roo.Element} el The context element
4793      * @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'})
4794      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4795      * @return {HTMLElement/Roo.Element} The new node or Element
4796      */
4797     insertAfter : function(el, values, returnElement){
4798         return this.doInsert('afterEnd', el, values, returnElement);
4799     },
4800     
4801     /**
4802      * Applies the supplied values to the template and appends the new node(s) to el.
4803      * @param {String/HTMLElement/Roo.Element} el The context element
4804      * @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'})
4805      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4806      * @return {HTMLElement/Roo.Element} The new node or Element
4807      */
4808     append : function(el, values, returnElement){
4809         return this.doInsert('beforeEnd', el, values, returnElement);
4810     },
4811
4812     doInsert : function(where, el, values, returnEl){
4813         el = Roo.getDom(el);
4814         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4815         return returnEl ? Roo.get(newNode, true) : newNode;
4816     },
4817
4818     /**
4819      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4820      * @param {String/HTMLElement/Roo.Element} el The context element
4821      * @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'})
4822      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4823      * @return {HTMLElement/Roo.Element} The new node or Element
4824      */
4825     overwrite : function(el, values, returnElement){
4826         el = Roo.getDom(el);
4827         el.innerHTML = this.applyTemplate(values);
4828         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4829     }
4830 };
4831 /**
4832  * Alias for {@link #applyTemplate}
4833  * @method
4834  */
4835 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4836
4837 // backwards compat
4838 Roo.DomHelper.Template = Roo.Template;
4839
4840 /**
4841  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4842  * @param {String/HTMLElement} el A DOM element or its id
4843  * @returns {Roo.Template} The created template
4844  * @static
4845  */
4846 Roo.Template.from = function(el){
4847     el = Roo.getDom(el);
4848     return new Roo.Template(el.value || el.innerHTML);
4849 };/*
4850  * Based on:
4851  * Ext JS Library 1.1.1
4852  * Copyright(c) 2006-2007, Ext JS, LLC.
4853  *
4854  * Originally Released Under LGPL - original licence link has changed is not relivant.
4855  *
4856  * Fork - LGPL
4857  * <script type="text/javascript">
4858  */
4859  
4860
4861 /*
4862  * This is code is also distributed under MIT license for use
4863  * with jQuery and prototype JavaScript libraries.
4864  */
4865 /**
4866  * @class Roo.DomQuery
4867 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).
4868 <p>
4869 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>
4870
4871 <p>
4872 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.
4873 </p>
4874 <h4>Element Selectors:</h4>
4875 <ul class="list">
4876     <li> <b>*</b> any element</li>
4877     <li> <b>E</b> an element with the tag E</li>
4878     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4879     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4880     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4881     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4882 </ul>
4883 <h4>Attribute Selectors:</h4>
4884 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4885 <ul class="list">
4886     <li> <b>E[foo]</b> has an attribute "foo"</li>
4887     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4888     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4889     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4890     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4891     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4892     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4893 </ul>
4894 <h4>Pseudo Classes:</h4>
4895 <ul class="list">
4896     <li> <b>E:first-child</b> E is the first child of its parent</li>
4897     <li> <b>E:last-child</b> E is the last child of its parent</li>
4898     <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>
4899     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4900     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4901     <li> <b>E:only-child</b> E is the only child of its parent</li>
4902     <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>
4903     <li> <b>E:first</b> the first E in the resultset</li>
4904     <li> <b>E:last</b> the last E in the resultset</li>
4905     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4906     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4907     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4908     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4909     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4910     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4911     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4912     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4913     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4914 </ul>
4915 <h4>CSS Value Selectors:</h4>
4916 <ul class="list">
4917     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4918     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4919     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4920     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4921     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4922     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4923 </ul>
4924  * @singleton
4925  */
4926 Roo.DomQuery = function(){
4927     var cache = {}, simpleCache = {}, valueCache = {};
4928     var nonSpace = /\S/;
4929     var trimRe = /^\s+|\s+$/g;
4930     var tplRe = /\{(\d+)\}/g;
4931     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4932     var tagTokenRe = /^(#)?([\w-\*]+)/;
4933     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4934
4935     function child(p, index){
4936         var i = 0;
4937         var n = p.firstChild;
4938         while(n){
4939             if(n.nodeType == 1){
4940                if(++i == index){
4941                    return n;
4942                }
4943             }
4944             n = n.nextSibling;
4945         }
4946         return null;
4947     };
4948
4949     function next(n){
4950         while((n = n.nextSibling) && n.nodeType != 1);
4951         return n;
4952     };
4953
4954     function prev(n){
4955         while((n = n.previousSibling) && n.nodeType != 1);
4956         return n;
4957     };
4958
4959     function children(d){
4960         var n = d.firstChild, ni = -1;
4961             while(n){
4962                 var nx = n.nextSibling;
4963                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4964                     d.removeChild(n);
4965                 }else{
4966                     n.nodeIndex = ++ni;
4967                 }
4968                 n = nx;
4969             }
4970             return this;
4971         };
4972
4973     function byClassName(c, a, v){
4974         if(!v){
4975             return c;
4976         }
4977         var r = [], ri = -1, cn;
4978         for(var i = 0, ci; ci = c[i]; i++){
4979             if((' '+ci.className+' ').indexOf(v) != -1){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function attrValue(n, attr){
4987         if(!n.tagName && typeof n.length != "undefined"){
4988             n = n[0];
4989         }
4990         if(!n){
4991             return null;
4992         }
4993         if(attr == "for"){
4994             return n.htmlFor;
4995         }
4996         if(attr == "class" || attr == "className"){
4997             return n.className;
4998         }
4999         return n.getAttribute(attr) || n[attr];
5000
5001     };
5002
5003     function getNodes(ns, mode, tagName){
5004         var result = [], ri = -1, cs;
5005         if(!ns){
5006             return result;
5007         }
5008         tagName = tagName || "*";
5009         if(typeof ns.getElementsByTagName != "undefined"){
5010             ns = [ns];
5011         }
5012         if(!mode){
5013             for(var i = 0, ni; ni = ns[i]; i++){
5014                 cs = ni.getElementsByTagName(tagName);
5015                 for(var j = 0, ci; ci = cs[j]; j++){
5016                     result[++ri] = ci;
5017                 }
5018             }
5019         }else if(mode == "/" || mode == ">"){
5020             var utag = tagName.toUpperCase();
5021             for(var i = 0, ni, cn; ni = ns[i]; i++){
5022                 cn = ni.children || ni.childNodes;
5023                 for(var j = 0, cj; cj = cn[j]; j++){
5024                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5025                         result[++ri] = cj;
5026                     }
5027                 }
5028             }
5029         }else if(mode == "+"){
5030             var utag = tagName.toUpperCase();
5031             for(var i = 0, n; n = ns[i]; i++){
5032                 while((n = n.nextSibling) && n.nodeType != 1);
5033                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5034                     result[++ri] = n;
5035                 }
5036             }
5037         }else if(mode == "~"){
5038             for(var i = 0, n; n = ns[i]; i++){
5039                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5040                 if(n){
5041                     result[++ri] = n;
5042                 }
5043             }
5044         }
5045         return result;
5046     };
5047
5048     function concat(a, b){
5049         if(b.slice){
5050             return a.concat(b);
5051         }
5052         for(var i = 0, l = b.length; i < l; i++){
5053             a[a.length] = b[i];
5054         }
5055         return a;
5056     }
5057
5058     function byTag(cs, tagName){
5059         if(cs.tagName || cs == document){
5060             cs = [cs];
5061         }
5062         if(!tagName){
5063             return cs;
5064         }
5065         var r = [], ri = -1;
5066         tagName = tagName.toLowerCase();
5067         for(var i = 0, ci; ci = cs[i]; i++){
5068             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5069                 r[++ri] = ci;
5070             }
5071         }
5072         return r;
5073     };
5074
5075     function byId(cs, attr, id){
5076         if(cs.tagName || cs == document){
5077             cs = [cs];
5078         }
5079         if(!id){
5080             return cs;
5081         }
5082         var r = [], ri = -1;
5083         for(var i = 0,ci; ci = cs[i]; i++){
5084             if(ci && ci.id == id){
5085                 r[++ri] = ci;
5086                 return r;
5087             }
5088         }
5089         return r;
5090     };
5091
5092     function byAttribute(cs, attr, value, op, custom){
5093         var r = [], ri = -1, st = custom=="{";
5094         var f = Roo.DomQuery.operators[op];
5095         for(var i = 0, ci; ci = cs[i]; i++){
5096             var a;
5097             if(st){
5098                 a = Roo.DomQuery.getStyle(ci, attr);
5099             }
5100             else if(attr == "class" || attr == "className"){
5101                 a = ci.className;
5102             }else if(attr == "for"){
5103                 a = ci.htmlFor;
5104             }else if(attr == "href"){
5105                 a = ci.getAttribute("href", 2);
5106             }else{
5107                 a = ci.getAttribute(attr);
5108             }
5109             if((f && f(a, value)) || (!f && a)){
5110                 r[++ri] = ci;
5111             }
5112         }
5113         return r;
5114     };
5115
5116     function byPseudo(cs, name, value){
5117         return Roo.DomQuery.pseudos[name](cs, value);
5118     };
5119
5120     // This is for IE MSXML which does not support expandos.
5121     // IE runs the same speed using setAttribute, however FF slows way down
5122     // and Safari completely fails so they need to continue to use expandos.
5123     var isIE = window.ActiveXObject ? true : false;
5124
5125     // this eval is stop the compressor from
5126     // renaming the variable to something shorter
5127     
5128     /** eval:var:batch */
5129     var batch = 30803; 
5130
5131     var key = 30803;
5132
5133     function nodupIEXml(cs){
5134         var d = ++key;
5135         cs[0].setAttribute("_nodup", d);
5136         var r = [cs[0]];
5137         for(var i = 1, len = cs.length; i < len; i++){
5138             var c = cs[i];
5139             if(!c.getAttribute("_nodup") != d){
5140                 c.setAttribute("_nodup", d);
5141                 r[r.length] = c;
5142             }
5143         }
5144         for(var i = 0, len = cs.length; i < len; i++){
5145             cs[i].removeAttribute("_nodup");
5146         }
5147         return r;
5148     }
5149
5150     function nodup(cs){
5151         if(!cs){
5152             return [];
5153         }
5154         var len = cs.length, c, i, r = cs, cj, ri = -1;
5155         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5156             return cs;
5157         }
5158         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5159             return nodupIEXml(cs);
5160         }
5161         var d = ++key;
5162         cs[0]._nodup = d;
5163         for(i = 1; c = cs[i]; i++){
5164             if(c._nodup != d){
5165                 c._nodup = d;
5166             }else{
5167                 r = [];
5168                 for(var j = 0; j < i; j++){
5169                     r[++ri] = cs[j];
5170                 }
5171                 for(j = i+1; cj = cs[j]; j++){
5172                     if(cj._nodup != d){
5173                         cj._nodup = d;
5174                         r[++ri] = cj;
5175                     }
5176                 }
5177                 return r;
5178             }
5179         }
5180         return r;
5181     }
5182
5183     function quickDiffIEXml(c1, c2){
5184         var d = ++key;
5185         for(var i = 0, len = c1.length; i < len; i++){
5186             c1[i].setAttribute("_qdiff", d);
5187         }
5188         var r = [];
5189         for(var i = 0, len = c2.length; i < len; i++){
5190             if(c2[i].getAttribute("_qdiff") != d){
5191                 r[r.length] = c2[i];
5192             }
5193         }
5194         for(var i = 0, len = c1.length; i < len; i++){
5195            c1[i].removeAttribute("_qdiff");
5196         }
5197         return r;
5198     }
5199
5200     function quickDiff(c1, c2){
5201         var len1 = c1.length;
5202         if(!len1){
5203             return c2;
5204         }
5205         if(isIE && c1[0].selectSingleNode){
5206             return quickDiffIEXml(c1, c2);
5207         }
5208         var d = ++key;
5209         for(var i = 0; i < len1; i++){
5210             c1[i]._qdiff = d;
5211         }
5212         var r = [];
5213         for(var i = 0, len = c2.length; i < len; i++){
5214             if(c2[i]._qdiff != d){
5215                 r[r.length] = c2[i];
5216             }
5217         }
5218         return r;
5219     }
5220
5221     function quickId(ns, mode, root, id){
5222         if(ns == root){
5223            var d = root.ownerDocument || root;
5224            return d.getElementById(id);
5225         }
5226         ns = getNodes(ns, mode, "*");
5227         return byId(ns, null, id);
5228     }
5229
5230     return {
5231         getStyle : function(el, name){
5232             return Roo.fly(el).getStyle(name);
5233         },
5234         /**
5235          * Compiles a selector/xpath query into a reusable function. The returned function
5236          * takes one parameter "root" (optional), which is the context node from where the query should start.
5237          * @param {String} selector The selector/xpath query
5238          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5239          * @return {Function}
5240          */
5241         compile : function(path, type){
5242             type = type || "select";
5243             
5244             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5245             var q = path, mode, lq;
5246             var tk = Roo.DomQuery.matchers;
5247             var tklen = tk.length;
5248             var mm;
5249
5250             // accept leading mode switch
5251             var lmode = q.match(modeRe);
5252             if(lmode && lmode[1]){
5253                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5254                 q = q.replace(lmode[1], "");
5255             }
5256             // strip leading slashes
5257             while(path.substr(0, 1)=="/"){
5258                 path = path.substr(1);
5259             }
5260
5261             while(q && lq != q){
5262                 lq = q;
5263                 var tm = q.match(tagTokenRe);
5264                 if(type == "select"){
5265                     if(tm){
5266                         if(tm[1] == "#"){
5267                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5268                         }else{
5269                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5270                         }
5271                         q = q.replace(tm[0], "");
5272                     }else if(q.substr(0, 1) != '@'){
5273                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5274                     }
5275                 }else{
5276                     if(tm){
5277                         if(tm[1] == "#"){
5278                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5279                         }else{
5280                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5281                         }
5282                         q = q.replace(tm[0], "");
5283                     }
5284                 }
5285                 while(!(mm = q.match(modeRe))){
5286                     var matched = false;
5287                     for(var j = 0; j < tklen; j++){
5288                         var t = tk[j];
5289                         var m = q.match(t.re);
5290                         if(m){
5291                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5292                                                     return m[i];
5293                                                 });
5294                             q = q.replace(m[0], "");
5295                             matched = true;
5296                             break;
5297                         }
5298                     }
5299                     // prevent infinite loop on bad selector
5300                     if(!matched){
5301                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5302                     }
5303                 }
5304                 if(mm[1]){
5305                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5306                     q = q.replace(mm[1], "");
5307                 }
5308             }
5309             fn[fn.length] = "return nodup(n);\n}";
5310             
5311              /** 
5312               * list of variables that need from compression as they are used by eval.
5313              *  eval:var:batch 
5314              *  eval:var:nodup
5315              *  eval:var:byTag
5316              *  eval:var:ById
5317              *  eval:var:getNodes
5318              *  eval:var:quickId
5319              *  eval:var:mode
5320              *  eval:var:root
5321              *  eval:var:n
5322              *  eval:var:byClassName
5323              *  eval:var:byPseudo
5324              *  eval:var:byAttribute
5325              *  eval:var:attrValue
5326              * 
5327              **/ 
5328             eval(fn.join(""));
5329             return f;
5330         },
5331
5332         /**
5333          * Selects a group of elements.
5334          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5335          * @param {Node} root (optional) The start of the query (defaults to document).
5336          * @return {Array}
5337          */
5338         select : function(path, root, type){
5339             if(!root || root == document){
5340                 root = document;
5341             }
5342             if(typeof root == "string"){
5343                 root = document.getElementById(root);
5344             }
5345             var paths = path.split(",");
5346             var results = [];
5347             for(var i = 0, len = paths.length; i < len; i++){
5348                 var p = paths[i].replace(trimRe, "");
5349                 if(!cache[p]){
5350                     cache[p] = Roo.DomQuery.compile(p);
5351                     if(!cache[p]){
5352                         throw p + " is not a valid selector";
5353                     }
5354                 }
5355                 var result = cache[p](root);
5356                 if(result && result != document){
5357                     results = results.concat(result);
5358                 }
5359             }
5360             if(paths.length > 1){
5361                 return nodup(results);
5362             }
5363             return results;
5364         },
5365
5366         /**
5367          * Selects a single element.
5368          * @param {String} selector The selector/xpath query
5369          * @param {Node} root (optional) The start of the query (defaults to document).
5370          * @return {Element}
5371          */
5372         selectNode : function(path, root){
5373             return Roo.DomQuery.select(path, root)[0];
5374         },
5375
5376         /**
5377          * Selects the value of a node, optionally replacing null with the defaultValue.
5378          * @param {String} selector The selector/xpath query
5379          * @param {Node} root (optional) The start of the query (defaults to document).
5380          * @param {String} defaultValue
5381          */
5382         selectValue : function(path, root, defaultValue){
5383             path = path.replace(trimRe, "");
5384             if(!valueCache[path]){
5385                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5386             }
5387             var n = valueCache[path](root);
5388             n = n[0] ? n[0] : n;
5389             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5390             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5391         },
5392
5393         /**
5394          * Selects the value of a node, parsing integers and floats.
5395          * @param {String} selector The selector/xpath query
5396          * @param {Node} root (optional) The start of the query (defaults to document).
5397          * @param {Number} defaultValue
5398          * @return {Number}
5399          */
5400         selectNumber : function(path, root, defaultValue){
5401             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5402             return parseFloat(v);
5403         },
5404
5405         /**
5406          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5407          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5408          * @param {String} selector The simple selector to test
5409          * @return {Boolean}
5410          */
5411         is : function(el, ss){
5412             if(typeof el == "string"){
5413                 el = document.getElementById(el);
5414             }
5415             var isArray = (el instanceof Array);
5416             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5417             return isArray ? (result.length == el.length) : (result.length > 0);
5418         },
5419
5420         /**
5421          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5422          * @param {Array} el An array of elements to filter
5423          * @param {String} selector The simple selector to test
5424          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5425          * the selector instead of the ones that match
5426          * @return {Array}
5427          */
5428         filter : function(els, ss, nonMatches){
5429             ss = ss.replace(trimRe, "");
5430             if(!simpleCache[ss]){
5431                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5432             }
5433             var result = simpleCache[ss](els);
5434             return nonMatches ? quickDiff(result, els) : result;
5435         },
5436
5437         /**
5438          * Collection of matching regular expressions and code snippets.
5439          */
5440         matchers : [{
5441                 re: /^\.([\w-]+)/,
5442                 select: 'n = byClassName(n, null, " {1} ");'
5443             }, {
5444                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5445                 select: 'n = byPseudo(n, "{1}", "{2}");'
5446             },{
5447                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5448                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5449             }, {
5450                 re: /^#([\w-]+)/,
5451                 select: 'n = byId(n, null, "{1}");'
5452             },{
5453                 re: /^@([\w-]+)/,
5454                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5455             }
5456         ],
5457
5458         /**
5459          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5460          * 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;.
5461          */
5462         operators : {
5463             "=" : function(a, v){
5464                 return a == v;
5465             },
5466             "!=" : function(a, v){
5467                 return a != v;
5468             },
5469             "^=" : function(a, v){
5470                 return a && a.substr(0, v.length) == v;
5471             },
5472             "$=" : function(a, v){
5473                 return a && a.substr(a.length-v.length) == v;
5474             },
5475             "*=" : function(a, v){
5476                 return a && a.indexOf(v) !== -1;
5477             },
5478             "%=" : function(a, v){
5479                 return (a % v) == 0;
5480             },
5481             "|=" : function(a, v){
5482                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5483             },
5484             "~=" : function(a, v){
5485                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5486             }
5487         },
5488
5489         /**
5490          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5491          * and the argument (if any) supplied in the selector.
5492          */
5493         pseudos : {
5494             "first-child" : function(c){
5495                 var r = [], ri = -1, n;
5496                 for(var i = 0, ci; ci = n = c[i]; i++){
5497                     while((n = n.previousSibling) && n.nodeType != 1);
5498                     if(!n){
5499                         r[++ri] = ci;
5500                     }
5501                 }
5502                 return r;
5503             },
5504
5505             "last-child" : function(c){
5506                 var r = [], ri = -1, n;
5507                 for(var i = 0, ci; ci = n = c[i]; i++){
5508                     while((n = n.nextSibling) && n.nodeType != 1);
5509                     if(!n){
5510                         r[++ri] = ci;
5511                     }
5512                 }
5513                 return r;
5514             },
5515
5516             "nth-child" : function(c, a) {
5517                 var r = [], ri = -1;
5518                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5519                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5520                 for(var i = 0, n; n = c[i]; i++){
5521                     var pn = n.parentNode;
5522                     if (batch != pn._batch) {
5523                         var j = 0;
5524                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5525                             if(cn.nodeType == 1){
5526                                cn.nodeIndex = ++j;
5527                             }
5528                         }
5529                         pn._batch = batch;
5530                     }
5531                     if (f == 1) {
5532                         if (l == 0 || n.nodeIndex == l){
5533                             r[++ri] = n;
5534                         }
5535                     } else if ((n.nodeIndex + l) % f == 0){
5536                         r[++ri] = n;
5537                     }
5538                 }
5539
5540                 return r;
5541             },
5542
5543             "only-child" : function(c){
5544                 var r = [], ri = -1;;
5545                 for(var i = 0, ci; ci = c[i]; i++){
5546                     if(!prev(ci) && !next(ci)){
5547                         r[++ri] = ci;
5548                     }
5549                 }
5550                 return r;
5551             },
5552
5553             "empty" : function(c){
5554                 var r = [], ri = -1;
5555                 for(var i = 0, ci; ci = c[i]; i++){
5556                     var cns = ci.childNodes, j = 0, cn, empty = true;
5557                     while(cn = cns[j]){
5558                         ++j;
5559                         if(cn.nodeType == 1 || cn.nodeType == 3){
5560                             empty = false;
5561                             break;
5562                         }
5563                     }
5564                     if(empty){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "contains" : function(c, v){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "nodeValue" : function(c, v){
5582                 var r = [], ri = -1;
5583                 for(var i = 0, ci; ci = c[i]; i++){
5584                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5585                         r[++ri] = ci;
5586                     }
5587                 }
5588                 return r;
5589             },
5590
5591             "checked" : function(c){
5592                 var r = [], ri = -1;
5593                 for(var i = 0, ci; ci = c[i]; i++){
5594                     if(ci.checked == true){
5595                         r[++ri] = ci;
5596                     }
5597                 }
5598                 return r;
5599             },
5600
5601             "not" : function(c, ss){
5602                 return Roo.DomQuery.filter(c, ss, true);
5603             },
5604
5605             "odd" : function(c){
5606                 return this["nth-child"](c, "odd");
5607             },
5608
5609             "even" : function(c){
5610                 return this["nth-child"](c, "even");
5611             },
5612
5613             "nth" : function(c, a){
5614                 return c[a-1] || [];
5615             },
5616
5617             "first" : function(c){
5618                 return c[0] || [];
5619             },
5620
5621             "last" : function(c){
5622                 return c[c.length-1] || [];
5623             },
5624
5625             "has" : function(c, ss){
5626                 var s = Roo.DomQuery.select;
5627                 var r = [], ri = -1;
5628                 for(var i = 0, ci; ci = c[i]; i++){
5629                     if(s(ss, ci).length > 0){
5630                         r[++ri] = ci;
5631                     }
5632                 }
5633                 return r;
5634             },
5635
5636             "next" : function(c, ss){
5637                 var is = Roo.DomQuery.is;
5638                 var r = [], ri = -1;
5639                 for(var i = 0, ci; ci = c[i]; i++){
5640                     var n = next(ci);
5641                     if(n && is(n, ss)){
5642                         r[++ri] = ci;
5643                     }
5644                 }
5645                 return r;
5646             },
5647
5648             "prev" : function(c, ss){
5649                 var is = Roo.DomQuery.is;
5650                 var r = [], ri = -1;
5651                 for(var i = 0, ci; ci = c[i]; i++){
5652                     var n = prev(ci);
5653                     if(n && is(n, ss)){
5654                         r[++ri] = ci;
5655                     }
5656                 }
5657                 return r;
5658             }
5659         }
5660     };
5661 }();
5662
5663 /**
5664  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5665  * @param {String} path The selector/xpath query
5666  * @param {Node} root (optional) The start of the query (defaults to document).
5667  * @return {Array}
5668  * @member Roo
5669  * @method query
5670  */
5671 Roo.query = Roo.DomQuery.select;
5672 /*
5673  * Based on:
5674  * Ext JS Library 1.1.1
5675  * Copyright(c) 2006-2007, Ext JS, LLC.
5676  *
5677  * Originally Released Under LGPL - original licence link has changed is not relivant.
5678  *
5679  * Fork - LGPL
5680  * <script type="text/javascript">
5681  */
5682
5683 /**
5684  * @class Roo.util.Observable
5685  * Base class that provides a common interface for publishing events. Subclasses are expected to
5686  * to have a property "events" with all the events defined.<br>
5687  * For example:
5688  * <pre><code>
5689  Employee = function(name){
5690     this.name = name;
5691     this.addEvents({
5692         "fired" : true,
5693         "quit" : true
5694     });
5695  }
5696  Roo.extend(Employee, Roo.util.Observable);
5697 </code></pre>
5698  * @param {Object} config properties to use (incuding events / listeners)
5699  */
5700
5701 Roo.util.Observable = function(cfg){
5702     
5703     cfg = cfg|| {};
5704     this.addEvents(cfg.events || {});
5705     if (cfg.events) {
5706         delete cfg.events; // make sure
5707     }
5708      
5709     Roo.apply(this, cfg);
5710     
5711     if(this.listeners){
5712         this.on(this.listeners);
5713         delete this.listeners;
5714     }
5715 };
5716 Roo.util.Observable.prototype = {
5717     /** 
5718  * @cfg {Object} listeners  list of events and functions to call for this object, 
5719  * For example :
5720  * <pre><code>
5721     listeners :  { 
5722        'click' : function(e) {
5723            ..... 
5724         } ,
5725         .... 
5726     } 
5727   </code></pre>
5728  */
5729     
5730     
5731     /**
5732      * Fires the specified event with the passed parameters (minus the event name).
5733      * @param {String} eventName
5734      * @param {Object...} args Variable number of parameters are passed to handlers
5735      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5736      */
5737     fireEvent : function(){
5738         var ce = this.events[arguments[0].toLowerCase()];
5739         if(typeof ce == "object"){
5740             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5741         }else{
5742             return true;
5743         }
5744     },
5745
5746     // private
5747     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5748
5749     /**
5750      * Appends an event handler to this component
5751      * @param {String}   eventName The type of event to listen for
5752      * @param {Function} handler The method the event invokes
5753      * @param {Object}   scope (optional) The scope in which to execute the handler
5754      * function. The handler function's "this" context.
5755      * @param {Object}   options (optional) An object containing handler configuration
5756      * properties. This may contain any of the following properties:<ul>
5757      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5758      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5759      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5760      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5761      * by the specified number of milliseconds. If the event fires again within that time, the original
5762      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5763      * </ul><br>
5764      * <p>
5765      * <b>Combining Options</b><br>
5766      * Using the options argument, it is possible to combine different types of listeners:<br>
5767      * <br>
5768      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5769                 <pre><code>
5770                 el.on('click', this.onClick, this, {
5771                         single: true,
5772                 delay: 100,
5773                 forumId: 4
5774                 });
5775                 </code></pre>
5776      * <p>
5777      * <b>Attaching multiple handlers in 1 call</b><br>
5778      * The method also allows for a single argument to be passed which is a config object containing properties
5779      * which specify multiple handlers.
5780      * <pre><code>
5781                 el.on({
5782                         'click': {
5783                         fn: this.onClick,
5784                         scope: this,
5785                         delay: 100
5786                 }, 
5787                 'mouseover': {
5788                         fn: this.onMouseOver,
5789                         scope: this
5790                 },
5791                 'mouseout': {
5792                         fn: this.onMouseOut,
5793                         scope: this
5794                 }
5795                 });
5796                 </code></pre>
5797      * <p>
5798      * Or a shorthand syntax which passes the same scope object to all handlers:
5799         <pre><code>
5800                 el.on({
5801                         'click': this.onClick,
5802                 'mouseover': this.onMouseOver,
5803                 'mouseout': this.onMouseOut,
5804                 scope: this
5805                 });
5806                 </code></pre>
5807      */
5808     addListener : function(eventName, fn, scope, o){
5809         if(typeof eventName == "object"){
5810             o = eventName;
5811             for(var e in o){
5812                 if(this.filterOptRe.test(e)){
5813                     continue;
5814                 }
5815                 if(typeof o[e] == "function"){
5816                     // shared options
5817                     this.addListener(e, o[e], o.scope,  o);
5818                 }else{
5819                     // individual options
5820                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5821                 }
5822             }
5823             return;
5824         }
5825         o = (!o || typeof o == "boolean") ? {} : o;
5826         eventName = eventName.toLowerCase();
5827         var ce = this.events[eventName] || true;
5828         if(typeof ce == "boolean"){
5829             ce = new Roo.util.Event(this, eventName);
5830             this.events[eventName] = ce;
5831         }
5832         ce.addListener(fn, scope, o);
5833     },
5834
5835     /**
5836      * Removes a listener
5837      * @param {String}   eventName     The type of event to listen for
5838      * @param {Function} handler        The handler to remove
5839      * @param {Object}   scope  (optional) The scope (this object) for the handler
5840      */
5841     removeListener : function(eventName, fn, scope){
5842         var ce = this.events[eventName.toLowerCase()];
5843         if(typeof ce == "object"){
5844             ce.removeListener(fn, scope);
5845         }
5846     },
5847
5848     /**
5849      * Removes all listeners for this object
5850      */
5851     purgeListeners : function(){
5852         for(var evt in this.events){
5853             if(typeof this.events[evt] == "object"){
5854                  this.events[evt].clearListeners();
5855             }
5856         }
5857     },
5858
5859     relayEvents : function(o, events){
5860         var createHandler = function(ename){
5861             return function(){
5862                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5863             };
5864         };
5865         for(var i = 0, len = events.length; i < len; i++){
5866             var ename = events[i];
5867             if(!this.events[ename]){ this.events[ename] = true; };
5868             o.on(ename, createHandler(ename), this);
5869         }
5870     },
5871
5872     /**
5873      * Used to define events on this Observable
5874      * @param {Object} object The object with the events defined
5875      */
5876     addEvents : function(o){
5877         if(!this.events){
5878             this.events = {};
5879         }
5880         Roo.applyIf(this.events, o);
5881     },
5882
5883     /**
5884      * Checks to see if this object has any listeners for a specified event
5885      * @param {String} eventName The name of the event to check for
5886      * @return {Boolean} True if the event is being listened for, else false
5887      */
5888     hasListener : function(eventName){
5889         var e = this.events[eventName];
5890         return typeof e == "object" && e.listeners.length > 0;
5891     }
5892 };
5893 /**
5894  * Appends an event handler to this element (shorthand for addListener)
5895  * @param {String}   eventName     The type of event to listen for
5896  * @param {Function} handler        The method the event invokes
5897  * @param {Object}   scope (optional) The scope in which to execute the handler
5898  * function. The handler function's "this" context.
5899  * @param {Object}   options  (optional)
5900  * @method
5901  */
5902 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5903 /**
5904  * Removes a listener (shorthand for removeListener)
5905  * @param {String}   eventName     The type of event to listen for
5906  * @param {Function} handler        The handler to remove
5907  * @param {Object}   scope  (optional) The scope (this object) for the handler
5908  * @method
5909  */
5910 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5911
5912 /**
5913  * Starts capture on the specified Observable. All events will be passed
5914  * to the supplied function with the event name + standard signature of the event
5915  * <b>before</b> the event is fired. If the supplied function returns false,
5916  * the event will not fire.
5917  * @param {Observable} o The Observable to capture
5918  * @param {Function} fn The function to call
5919  * @param {Object} scope (optional) The scope (this object) for the fn
5920  * @static
5921  */
5922 Roo.util.Observable.capture = function(o, fn, scope){
5923     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5924 };
5925
5926 /**
5927  * Removes <b>all</b> added captures from the Observable.
5928  * @param {Observable} o The Observable to release
5929  * @static
5930  */
5931 Roo.util.Observable.releaseCapture = function(o){
5932     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5933 };
5934
5935 (function(){
5936
5937     var createBuffered = function(h, o, scope){
5938         var task = new Roo.util.DelayedTask();
5939         return function(){
5940             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5941         };
5942     };
5943
5944     var createSingle = function(h, e, fn, scope){
5945         return function(){
5946             e.removeListener(fn, scope);
5947             return h.apply(scope, arguments);
5948         };
5949     };
5950
5951     var createDelayed = function(h, o, scope){
5952         return function(){
5953             var args = Array.prototype.slice.call(arguments, 0);
5954             setTimeout(function(){
5955                 h.apply(scope, args);
5956             }, o.delay || 10);
5957         };
5958     };
5959
5960     Roo.util.Event = function(obj, name){
5961         this.name = name;
5962         this.obj = obj;
5963         this.listeners = [];
5964     };
5965
5966     Roo.util.Event.prototype = {
5967         addListener : function(fn, scope, options){
5968             var o = options || {};
5969             scope = scope || this.obj;
5970             if(!this.isListening(fn, scope)){
5971                 var l = {fn: fn, scope: scope, options: o};
5972                 var h = fn;
5973                 if(o.delay){
5974                     h = createDelayed(h, o, scope);
5975                 }
5976                 if(o.single){
5977                     h = createSingle(h, this, fn, scope);
5978                 }
5979                 if(o.buffer){
5980                     h = createBuffered(h, o, scope);
5981                 }
5982                 l.fireFn = h;
5983                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5984                     this.listeners.push(l);
5985                 }else{
5986                     this.listeners = this.listeners.slice(0);
5987                     this.listeners.push(l);
5988                 }
5989             }
5990         },
5991
5992         findListener : function(fn, scope){
5993             scope = scope || this.obj;
5994             var ls = this.listeners;
5995             for(var i = 0, len = ls.length; i < len; i++){
5996                 var l = ls[i];
5997                 if(l.fn == fn && l.scope == scope){
5998                     return i;
5999                 }
6000             }
6001             return -1;
6002         },
6003
6004         isListening : function(fn, scope){
6005             return this.findListener(fn, scope) != -1;
6006         },
6007
6008         removeListener : function(fn, scope){
6009             var index;
6010             if((index = this.findListener(fn, scope)) != -1){
6011                 if(!this.firing){
6012                     this.listeners.splice(index, 1);
6013                 }else{
6014                     this.listeners = this.listeners.slice(0);
6015                     this.listeners.splice(index, 1);
6016                 }
6017                 return true;
6018             }
6019             return false;
6020         },
6021
6022         clearListeners : function(){
6023             this.listeners = [];
6024         },
6025
6026         fire : function(){
6027             var ls = this.listeners, scope, len = ls.length;
6028             if(len > 0){
6029                 this.firing = true;
6030                 var args = Array.prototype.slice.call(arguments, 0);
6031                 for(var i = 0; i < len; i++){
6032                     var l = ls[i];
6033                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6034                         this.firing = false;
6035                         return false;
6036                     }
6037                 }
6038                 this.firing = false;
6039             }
6040             return true;
6041         }
6042     };
6043 })();/*
6044  * Based on:
6045  * Ext JS Library 1.1.1
6046  * Copyright(c) 2006-2007, Ext JS, LLC.
6047  *
6048  * Originally Released Under LGPL - original licence link has changed is not relivant.
6049  *
6050  * Fork - LGPL
6051  * <script type="text/javascript">
6052  */
6053
6054 /**
6055  * @class Roo.EventManager
6056  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6057  * several useful events directly.
6058  * See {@link Roo.EventObject} for more details on normalized event objects.
6059  * @singleton
6060  */
6061 Roo.EventManager = function(){
6062     var docReadyEvent, docReadyProcId, docReadyState = false;
6063     var resizeEvent, resizeTask, textEvent, textSize;
6064     var E = Roo.lib.Event;
6065     var D = Roo.lib.Dom;
6066
6067     
6068     
6069
6070     var fireDocReady = function(){
6071         if(!docReadyState){
6072             docReadyState = true;
6073             Roo.isReady = true;
6074             if(docReadyProcId){
6075                 clearInterval(docReadyProcId);
6076             }
6077             if(Roo.isGecko || Roo.isOpera) {
6078                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6079             }
6080             if(Roo.isIE){
6081                 var defer = document.getElementById("ie-deferred-loader");
6082                 if(defer){
6083                     defer.onreadystatechange = null;
6084                     defer.parentNode.removeChild(defer);
6085                 }
6086             }
6087             if(docReadyEvent){
6088                 docReadyEvent.fire();
6089                 docReadyEvent.clearListeners();
6090             }
6091         }
6092     };
6093     
6094     var initDocReady = function(){
6095         docReadyEvent = new Roo.util.Event();
6096         if(Roo.isGecko || Roo.isOpera) {
6097             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6098         }else if(Roo.isIE){
6099             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6100             var defer = document.getElementById("ie-deferred-loader");
6101             defer.onreadystatechange = function(){
6102                 if(this.readyState == "complete"){
6103                     fireDocReady();
6104                 }
6105             };
6106         }else if(Roo.isSafari){ 
6107             docReadyProcId = setInterval(function(){
6108                 var rs = document.readyState;
6109                 if(rs == "complete") {
6110                     fireDocReady();     
6111                  }
6112             }, 10);
6113         }
6114         // no matter what, make sure it fires on load
6115         E.on(window, "load", fireDocReady);
6116     };
6117
6118     var createBuffered = function(h, o){
6119         var task = new Roo.util.DelayedTask(h);
6120         return function(e){
6121             // create new event object impl so new events don't wipe out properties
6122             e = new Roo.EventObjectImpl(e);
6123             task.delay(o.buffer, h, null, [e]);
6124         };
6125     };
6126
6127     var createSingle = function(h, el, ename, fn){
6128         return function(e){
6129             Roo.EventManager.removeListener(el, ename, fn);
6130             h(e);
6131         };
6132     };
6133
6134     var createDelayed = function(h, o){
6135         return function(e){
6136             // create new event object impl so new events don't wipe out properties
6137             e = new Roo.EventObjectImpl(e);
6138             setTimeout(function(){
6139                 h(e);
6140             }, o.delay || 10);
6141         };
6142     };
6143     var transitionEndVal = false;
6144     
6145     var transitionEnd = function()
6146     {
6147         if (transitionEndVal) {
6148             return transitionEndVal;
6149         }
6150         var el = document.createElement('div');
6151
6152         var transEndEventNames = {
6153             WebkitTransition : 'webkitTransitionEnd',
6154             MozTransition    : 'transitionend',
6155             OTransition      : 'oTransitionEnd otransitionend',
6156             transition       : 'transitionend'
6157         };
6158     
6159         for (var name in transEndEventNames) {
6160             if (el.style[name] !== undefined) {
6161                 transitionEndVal = transEndEventNames[name];
6162                 return  transitionEndVal ;
6163             }
6164         }
6165     }
6166     
6167
6168     var listen = function(element, ename, opt, fn, scope){
6169         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6170         fn = fn || o.fn; scope = scope || o.scope;
6171         var el = Roo.getDom(element);
6172         
6173         
6174         if(!el){
6175             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6176         }
6177         
6178         if (ename == 'transitionend') {
6179             ename = transitionEnd();
6180         }
6181         var h = function(e){
6182             e = Roo.EventObject.setEvent(e);
6183             var t;
6184             if(o.delegate){
6185                 t = e.getTarget(o.delegate, el);
6186                 if(!t){
6187                     return;
6188                 }
6189             }else{
6190                 t = e.target;
6191             }
6192             if(o.stopEvent === true){
6193                 e.stopEvent();
6194             }
6195             if(o.preventDefault === true){
6196                e.preventDefault();
6197             }
6198             if(o.stopPropagation === true){
6199                 e.stopPropagation();
6200             }
6201
6202             if(o.normalized === false){
6203                 e = e.browserEvent;
6204             }
6205
6206             fn.call(scope || el, e, t, o);
6207         };
6208         if(o.delay){
6209             h = createDelayed(h, o);
6210         }
6211         if(o.single){
6212             h = createSingle(h, el, ename, fn);
6213         }
6214         if(o.buffer){
6215             h = createBuffered(h, o);
6216         }
6217         fn._handlers = fn._handlers || [];
6218         
6219         
6220         fn._handlers.push([Roo.id(el), ename, h]);
6221         
6222         
6223          
6224         E.on(el, ename, h);
6225         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6226             el.addEventListener("DOMMouseScroll", h, false);
6227             E.on(window, 'unload', function(){
6228                 el.removeEventListener("DOMMouseScroll", h, false);
6229             });
6230         }
6231         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6232             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6233         }
6234         return h;
6235     };
6236
6237     var stopListening = function(el, ename, fn){
6238         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6239         if(hds){
6240             for(var i = 0, len = hds.length; i < len; i++){
6241                 var h = hds[i];
6242                 if(h[0] == id && h[1] == ename){
6243                     hd = h[2];
6244                     hds.splice(i, 1);
6245                     break;
6246                 }
6247             }
6248         }
6249         E.un(el, ename, hd);
6250         el = Roo.getDom(el);
6251         if(ename == "mousewheel" && el.addEventListener){
6252             el.removeEventListener("DOMMouseScroll", hd, false);
6253         }
6254         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6255             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6256         }
6257     };
6258
6259     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6260     
6261     var pub = {
6262         
6263         
6264         /** 
6265          * Fix for doc tools
6266          * @scope Roo.EventManager
6267          */
6268         
6269         
6270         /** 
6271          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6272          * object with a Roo.EventObject
6273          * @param {Function} fn        The method the event invokes
6274          * @param {Object}   scope    An object that becomes the scope of the handler
6275          * @param {boolean}  override If true, the obj passed in becomes
6276          *                             the execution scope of the listener
6277          * @return {Function} The wrapped function
6278          * @deprecated
6279          */
6280         wrap : function(fn, scope, override){
6281             return function(e){
6282                 Roo.EventObject.setEvent(e);
6283                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6284             };
6285         },
6286         
6287         /**
6288      * Appends an event handler to an element (shorthand for addListener)
6289      * @param {String/HTMLElement}   element        The html element or id to assign the
6290      * @param {String}   eventName The type of event to listen for
6291      * @param {Function} handler The method the event invokes
6292      * @param {Object}   scope (optional) The scope in which to execute the handler
6293      * function. The handler function's "this" context.
6294      * @param {Object}   options (optional) An object containing handler configuration
6295      * properties. This may contain any of the following properties:<ul>
6296      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6297      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6298      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6299      * <li>preventDefault {Boolean} True to prevent the default action</li>
6300      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6301      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6302      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6303      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6304      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6305      * by the specified number of milliseconds. If the event fires again within that time, the original
6306      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6307      * </ul><br>
6308      * <p>
6309      * <b>Combining Options</b><br>
6310      * Using the options argument, it is possible to combine different types of listeners:<br>
6311      * <br>
6312      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6313      * Code:<pre><code>
6314 el.on('click', this.onClick, this, {
6315     single: true,
6316     delay: 100,
6317     stopEvent : true,
6318     forumId: 4
6319 });</code></pre>
6320      * <p>
6321      * <b>Attaching multiple handlers in 1 call</b><br>
6322       * The method also allows for a single argument to be passed which is a config object containing properties
6323      * which specify multiple handlers.
6324      * <p>
6325      * Code:<pre><code>
6326 el.on({
6327     'click' : {
6328         fn: this.onClick
6329         scope: this,
6330         delay: 100
6331     },
6332     'mouseover' : {
6333         fn: this.onMouseOver
6334         scope: this
6335     },
6336     'mouseout' : {
6337         fn: this.onMouseOut
6338         scope: this
6339     }
6340 });</code></pre>
6341      * <p>
6342      * Or a shorthand syntax:<br>
6343      * Code:<pre><code>
6344 el.on({
6345     'click' : this.onClick,
6346     'mouseover' : this.onMouseOver,
6347     'mouseout' : this.onMouseOut
6348     scope: this
6349 });</code></pre>
6350      */
6351         addListener : function(element, eventName, fn, scope, options){
6352             if(typeof eventName == "object"){
6353                 var o = eventName;
6354                 for(var e in o){
6355                     if(propRe.test(e)){
6356                         continue;
6357                     }
6358                     if(typeof o[e] == "function"){
6359                         // shared options
6360                         listen(element, e, o, o[e], o.scope);
6361                     }else{
6362                         // individual options
6363                         listen(element, e, o[e]);
6364                     }
6365                 }
6366                 return;
6367             }
6368             return listen(element, eventName, options, fn, scope);
6369         },
6370         
6371         /**
6372          * Removes an event handler
6373          *
6374          * @param {String/HTMLElement}   element        The id or html element to remove the 
6375          *                             event from
6376          * @param {String}   eventName     The type of event
6377          * @param {Function} fn
6378          * @return {Boolean} True if a listener was actually removed
6379          */
6380         removeListener : function(element, eventName, fn){
6381             return stopListening(element, eventName, fn);
6382         },
6383         
6384         /**
6385          * Fires when the document is ready (before onload and before images are loaded). Can be 
6386          * accessed shorthanded Roo.onReady().
6387          * @param {Function} fn        The method the event invokes
6388          * @param {Object}   scope    An  object that becomes the scope of the handler
6389          * @param {boolean}  options
6390          */
6391         onDocumentReady : function(fn, scope, options){
6392             if(docReadyState){ // if it already fired
6393                 docReadyEvent.addListener(fn, scope, options);
6394                 docReadyEvent.fire();
6395                 docReadyEvent.clearListeners();
6396                 return;
6397             }
6398             if(!docReadyEvent){
6399                 initDocReady();
6400             }
6401             docReadyEvent.addListener(fn, scope, options);
6402         },
6403         
6404         /**
6405          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6406          * @param {Function} fn        The method the event invokes
6407          * @param {Object}   scope    An object that becomes the scope of the handler
6408          * @param {boolean}  options
6409          */
6410         onWindowResize : function(fn, scope, options){
6411             if(!resizeEvent){
6412                 resizeEvent = new Roo.util.Event();
6413                 resizeTask = new Roo.util.DelayedTask(function(){
6414                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6415                 });
6416                 E.on(window, "resize", function(){
6417                     if(Roo.isIE){
6418                         resizeTask.delay(50);
6419                     }else{
6420                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6421                     }
6422                 });
6423             }
6424             resizeEvent.addListener(fn, scope, options);
6425         },
6426
6427         /**
6428          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6429          * @param {Function} fn        The method the event invokes
6430          * @param {Object}   scope    An object that becomes the scope of the handler
6431          * @param {boolean}  options
6432          */
6433         onTextResize : function(fn, scope, options){
6434             if(!textEvent){
6435                 textEvent = new Roo.util.Event();
6436                 var textEl = new Roo.Element(document.createElement('div'));
6437                 textEl.dom.className = 'x-text-resize';
6438                 textEl.dom.innerHTML = 'X';
6439                 textEl.appendTo(document.body);
6440                 textSize = textEl.dom.offsetHeight;
6441                 setInterval(function(){
6442                     if(textEl.dom.offsetHeight != textSize){
6443                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6444                     }
6445                 }, this.textResizeInterval);
6446             }
6447             textEvent.addListener(fn, scope, options);
6448         },
6449
6450         /**
6451          * Removes the passed window resize listener.
6452          * @param {Function} fn        The method the event invokes
6453          * @param {Object}   scope    The scope of handler
6454          */
6455         removeResizeListener : function(fn, scope){
6456             if(resizeEvent){
6457                 resizeEvent.removeListener(fn, scope);
6458             }
6459         },
6460
6461         // private
6462         fireResize : function(){
6463             if(resizeEvent){
6464                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6465             }   
6466         },
6467         /**
6468          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6469          */
6470         ieDeferSrc : false,
6471         /**
6472          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6473          */
6474         textResizeInterval : 50
6475     };
6476     
6477     /**
6478      * Fix for doc tools
6479      * @scopeAlias pub=Roo.EventManager
6480      */
6481     
6482      /**
6483      * Appends an event handler to an element (shorthand for addListener)
6484      * @param {String/HTMLElement}   element        The html element or id to assign the
6485      * @param {String}   eventName The type of event to listen for
6486      * @param {Function} handler The method the event invokes
6487      * @param {Object}   scope (optional) The scope in which to execute the handler
6488      * function. The handler function's "this" context.
6489      * @param {Object}   options (optional) An object containing handler configuration
6490      * properties. This may contain any of the following properties:<ul>
6491      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6492      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6493      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6494      * <li>preventDefault {Boolean} True to prevent the default action</li>
6495      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6496      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6497      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6498      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6499      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6500      * by the specified number of milliseconds. If the event fires again within that time, the original
6501      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6502      * </ul><br>
6503      * <p>
6504      * <b>Combining Options</b><br>
6505      * Using the options argument, it is possible to combine different types of listeners:<br>
6506      * <br>
6507      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6508      * Code:<pre><code>
6509 el.on('click', this.onClick, this, {
6510     single: true,
6511     delay: 100,
6512     stopEvent : true,
6513     forumId: 4
6514 });</code></pre>
6515      * <p>
6516      * <b>Attaching multiple handlers in 1 call</b><br>
6517       * The method also allows for a single argument to be passed which is a config object containing properties
6518      * which specify multiple handlers.
6519      * <p>
6520      * Code:<pre><code>
6521 el.on({
6522     'click' : {
6523         fn: this.onClick
6524         scope: this,
6525         delay: 100
6526     },
6527     'mouseover' : {
6528         fn: this.onMouseOver
6529         scope: this
6530     },
6531     'mouseout' : {
6532         fn: this.onMouseOut
6533         scope: this
6534     }
6535 });</code></pre>
6536      * <p>
6537      * Or a shorthand syntax:<br>
6538      * Code:<pre><code>
6539 el.on({
6540     'click' : this.onClick,
6541     'mouseover' : this.onMouseOver,
6542     'mouseout' : this.onMouseOut
6543     scope: this
6544 });</code></pre>
6545      */
6546     pub.on = pub.addListener;
6547     pub.un = pub.removeListener;
6548
6549     pub.stoppedMouseDownEvent = new Roo.util.Event();
6550     return pub;
6551 }();
6552 /**
6553   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6554   * @param {Function} fn        The method the event invokes
6555   * @param {Object}   scope    An  object that becomes the scope of the handler
6556   * @param {boolean}  override If true, the obj passed in becomes
6557   *                             the execution scope of the listener
6558   * @member Roo
6559   * @method onReady
6560  */
6561 Roo.onReady = Roo.EventManager.onDocumentReady;
6562
6563 Roo.onReady(function(){
6564     var bd = Roo.get(document.body);
6565     if(!bd){ return; }
6566
6567     var cls = [
6568             Roo.isIE ? "roo-ie"
6569             : Roo.isGecko ? "roo-gecko"
6570             : Roo.isOpera ? "roo-opera"
6571             : Roo.isSafari ? "roo-safari" : ""];
6572
6573     if(Roo.isMac){
6574         cls.push("roo-mac");
6575     }
6576     if(Roo.isLinux){
6577         cls.push("roo-linux");
6578     }
6579     if(Roo.isBorderBox){
6580         cls.push('roo-border-box');
6581     }
6582     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6583         var p = bd.dom.parentNode;
6584         if(p){
6585             p.className += ' roo-strict';
6586         }
6587     }
6588     bd.addClass(cls.join(' '));
6589 });
6590
6591 /**
6592  * @class Roo.EventObject
6593  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6594  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6595  * Example:
6596  * <pre><code>
6597  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6598     e.preventDefault();
6599     var target = e.getTarget();
6600     ...
6601  }
6602  var myDiv = Roo.get("myDiv");
6603  myDiv.on("click", handleClick);
6604  //or
6605  Roo.EventManager.on("myDiv", 'click', handleClick);
6606  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6607  </code></pre>
6608  * @singleton
6609  */
6610 Roo.EventObject = function(){
6611     
6612     var E = Roo.lib.Event;
6613     
6614     // safari keypress events for special keys return bad keycodes
6615     var safariKeys = {
6616         63234 : 37, // left
6617         63235 : 39, // right
6618         63232 : 38, // up
6619         63233 : 40, // down
6620         63276 : 33, // page up
6621         63277 : 34, // page down
6622         63272 : 46, // delete
6623         63273 : 36, // home
6624         63275 : 35  // end
6625     };
6626
6627     // normalize button clicks
6628     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6629                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6630
6631     Roo.EventObjectImpl = function(e){
6632         if(e){
6633             this.setEvent(e.browserEvent || e);
6634         }
6635     };
6636     Roo.EventObjectImpl.prototype = {
6637         /**
6638          * Used to fix doc tools.
6639          * @scope Roo.EventObject.prototype
6640          */
6641             
6642
6643         
6644         
6645         /** The normal browser event */
6646         browserEvent : null,
6647         /** The button pressed in a mouse event */
6648         button : -1,
6649         /** True if the shift key was down during the event */
6650         shiftKey : false,
6651         /** True if the control key was down during the event */
6652         ctrlKey : false,
6653         /** True if the alt key was down during the event */
6654         altKey : false,
6655
6656         /** Key constant 
6657         * @type Number */
6658         BACKSPACE : 8,
6659         /** Key constant 
6660         * @type Number */
6661         TAB : 9,
6662         /** Key constant 
6663         * @type Number */
6664         RETURN : 13,
6665         /** Key constant 
6666         * @type Number */
6667         ENTER : 13,
6668         /** Key constant 
6669         * @type Number */
6670         SHIFT : 16,
6671         /** Key constant 
6672         * @type Number */
6673         CONTROL : 17,
6674         /** Key constant 
6675         * @type Number */
6676         ESC : 27,
6677         /** Key constant 
6678         * @type Number */
6679         SPACE : 32,
6680         /** Key constant 
6681         * @type Number */
6682         PAGEUP : 33,
6683         /** Key constant 
6684         * @type Number */
6685         PAGEDOWN : 34,
6686         /** Key constant 
6687         * @type Number */
6688         END : 35,
6689         /** Key constant 
6690         * @type Number */
6691         HOME : 36,
6692         /** Key constant 
6693         * @type Number */
6694         LEFT : 37,
6695         /** Key constant 
6696         * @type Number */
6697         UP : 38,
6698         /** Key constant 
6699         * @type Number */
6700         RIGHT : 39,
6701         /** Key constant 
6702         * @type Number */
6703         DOWN : 40,
6704         /** Key constant 
6705         * @type Number */
6706         DELETE : 46,
6707         /** Key constant 
6708         * @type Number */
6709         F5 : 116,
6710
6711            /** @private */
6712         setEvent : function(e){
6713             if(e == this || (e && e.browserEvent)){ // already wrapped
6714                 return e;
6715             }
6716             this.browserEvent = e;
6717             if(e){
6718                 // normalize buttons
6719                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6720                 if(e.type == 'click' && this.button == -1){
6721                     this.button = 0;
6722                 }
6723                 this.type = e.type;
6724                 this.shiftKey = e.shiftKey;
6725                 // mac metaKey behaves like ctrlKey
6726                 this.ctrlKey = e.ctrlKey || e.metaKey;
6727                 this.altKey = e.altKey;
6728                 // in getKey these will be normalized for the mac
6729                 this.keyCode = e.keyCode;
6730                 // keyup warnings on firefox.
6731                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6732                 // cache the target for the delayed and or buffered events
6733                 this.target = E.getTarget(e);
6734                 // same for XY
6735                 this.xy = E.getXY(e);
6736             }else{
6737                 this.button = -1;
6738                 this.shiftKey = false;
6739                 this.ctrlKey = false;
6740                 this.altKey = false;
6741                 this.keyCode = 0;
6742                 this.charCode =0;
6743                 this.target = null;
6744                 this.xy = [0, 0];
6745             }
6746             return this;
6747         },
6748
6749         /**
6750          * Stop the event (preventDefault and stopPropagation)
6751          */
6752         stopEvent : function(){
6753             if(this.browserEvent){
6754                 if(this.browserEvent.type == 'mousedown'){
6755                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6756                 }
6757                 E.stopEvent(this.browserEvent);
6758             }
6759         },
6760
6761         /**
6762          * Prevents the browsers default handling of the event.
6763          */
6764         preventDefault : function(){
6765             if(this.browserEvent){
6766                 E.preventDefault(this.browserEvent);
6767             }
6768         },
6769
6770         /** @private */
6771         isNavKeyPress : function(){
6772             var k = this.keyCode;
6773             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6774             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6775         },
6776
6777         isSpecialKey : function(){
6778             var k = this.keyCode;
6779             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6780             (k == 16) || (k == 17) ||
6781             (k >= 18 && k <= 20) ||
6782             (k >= 33 && k <= 35) ||
6783             (k >= 36 && k <= 39) ||
6784             (k >= 44 && k <= 45);
6785         },
6786         /**
6787          * Cancels bubbling of the event.
6788          */
6789         stopPropagation : function(){
6790             if(this.browserEvent){
6791                 if(this.type == 'mousedown'){
6792                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6793                 }
6794                 E.stopPropagation(this.browserEvent);
6795             }
6796         },
6797
6798         /**
6799          * Gets the key code for the event.
6800          * @return {Number}
6801          */
6802         getCharCode : function(){
6803             return this.charCode || this.keyCode;
6804         },
6805
6806         /**
6807          * Returns a normalized keyCode for the event.
6808          * @return {Number} The key code
6809          */
6810         getKey : function(){
6811             var k = this.keyCode || this.charCode;
6812             return Roo.isSafari ? (safariKeys[k] || k) : k;
6813         },
6814
6815         /**
6816          * Gets the x coordinate of the event.
6817          * @return {Number}
6818          */
6819         getPageX : function(){
6820             return this.xy[0];
6821         },
6822
6823         /**
6824          * Gets the y coordinate of the event.
6825          * @return {Number}
6826          */
6827         getPageY : function(){
6828             return this.xy[1];
6829         },
6830
6831         /**
6832          * Gets the time of the event.
6833          * @return {Number}
6834          */
6835         getTime : function(){
6836             if(this.browserEvent){
6837                 return E.getTime(this.browserEvent);
6838             }
6839             return null;
6840         },
6841
6842         /**
6843          * Gets the page coordinates of the event.
6844          * @return {Array} The xy values like [x, y]
6845          */
6846         getXY : function(){
6847             return this.xy;
6848         },
6849
6850         /**
6851          * Gets the target for the event.
6852          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6853          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6854                 search as a number or element (defaults to 10 || document.body)
6855          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6856          * @return {HTMLelement}
6857          */
6858         getTarget : function(selector, maxDepth, returnEl){
6859             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6860         },
6861         /**
6862          * Gets the related target.
6863          * @return {HTMLElement}
6864          */
6865         getRelatedTarget : function(){
6866             if(this.browserEvent){
6867                 return E.getRelatedTarget(this.browserEvent);
6868             }
6869             return null;
6870         },
6871
6872         /**
6873          * Normalizes mouse wheel delta across browsers
6874          * @return {Number} The delta
6875          */
6876         getWheelDelta : function(){
6877             var e = this.browserEvent;
6878             var delta = 0;
6879             if(e.wheelDelta){ /* IE/Opera. */
6880                 delta = e.wheelDelta/120;
6881             }else if(e.detail){ /* Mozilla case. */
6882                 delta = -e.detail/3;
6883             }
6884             return delta;
6885         },
6886
6887         /**
6888          * Returns true if the control, meta, shift or alt key was pressed during this event.
6889          * @return {Boolean}
6890          */
6891         hasModifier : function(){
6892             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6893         },
6894
6895         /**
6896          * Returns true if the target of this event equals el or is a child of el
6897          * @param {String/HTMLElement/Element} el
6898          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6899          * @return {Boolean}
6900          */
6901         within : function(el, related){
6902             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6903             return t && Roo.fly(el).contains(t);
6904         },
6905
6906         getPoint : function(){
6907             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6908         }
6909     };
6910
6911     return new Roo.EventObjectImpl();
6912 }();
6913             
6914     /*
6915  * Based on:
6916  * Ext JS Library 1.1.1
6917  * Copyright(c) 2006-2007, Ext JS, LLC.
6918  *
6919  * Originally Released Under LGPL - original licence link has changed is not relivant.
6920  *
6921  * Fork - LGPL
6922  * <script type="text/javascript">
6923  */
6924
6925  
6926 // was in Composite Element!??!?!
6927  
6928 (function(){
6929     var D = Roo.lib.Dom;
6930     var E = Roo.lib.Event;
6931     var A = Roo.lib.Anim;
6932
6933     // local style camelizing for speed
6934     var propCache = {};
6935     var camelRe = /(-[a-z])/gi;
6936     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6937     var view = document.defaultView;
6938
6939 /**
6940  * @class Roo.Element
6941  * Represents an Element in the DOM.<br><br>
6942  * Usage:<br>
6943 <pre><code>
6944 var el = Roo.get("my-div");
6945
6946 // or with getEl
6947 var el = getEl("my-div");
6948
6949 // or with a DOM element
6950 var el = Roo.get(myDivElement);
6951 </code></pre>
6952  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6953  * each call instead of constructing a new one.<br><br>
6954  * <b>Animations</b><br />
6955  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6956  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6957 <pre>
6958 Option    Default   Description
6959 --------- --------  ---------------------------------------------
6960 duration  .35       The duration of the animation in seconds
6961 easing    easeOut   The YUI easing method
6962 callback  none      A function to execute when the anim completes
6963 scope     this      The scope (this) of the callback function
6964 </pre>
6965 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6966 * manipulate the animation. Here's an example:
6967 <pre><code>
6968 var el = Roo.get("my-div");
6969
6970 // no animation
6971 el.setWidth(100);
6972
6973 // default animation
6974 el.setWidth(100, true);
6975
6976 // animation with some options set
6977 el.setWidth(100, {
6978     duration: 1,
6979     callback: this.foo,
6980     scope: this
6981 });
6982
6983 // using the "anim" property to get the Anim object
6984 var opt = {
6985     duration: 1,
6986     callback: this.foo,
6987     scope: this
6988 };
6989 el.setWidth(100, opt);
6990 ...
6991 if(opt.anim.isAnimated()){
6992     opt.anim.stop();
6993 }
6994 </code></pre>
6995 * <b> Composite (Collections of) Elements</b><br />
6996  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6997  * @constructor Create a new Element directly.
6998  * @param {String/HTMLElement} element
6999  * @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).
7000  */
7001     Roo.Element = function(element, forceNew){
7002         var dom = typeof element == "string" ?
7003                 document.getElementById(element) : element;
7004         if(!dom){ // invalid id/element
7005             return null;
7006         }
7007         var id = dom.id;
7008         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7009             return Roo.Element.cache[id];
7010         }
7011
7012         /**
7013          * The DOM element
7014          * @type HTMLElement
7015          */
7016         this.dom = dom;
7017
7018         /**
7019          * The DOM element ID
7020          * @type String
7021          */
7022         this.id = id || Roo.id(dom);
7023     };
7024
7025     var El = Roo.Element;
7026
7027     El.prototype = {
7028         /**
7029          * The element's default display mode  (defaults to "")
7030          * @type String
7031          */
7032         originalDisplay : "",
7033
7034         visibilityMode : 1,
7035         /**
7036          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7037          * @type String
7038          */
7039         defaultUnit : "px",
7040         /**
7041          * Sets the element's visibility mode. When setVisible() is called it
7042          * will use this to determine whether to set the visibility or the display property.
7043          * @param visMode Element.VISIBILITY or Element.DISPLAY
7044          * @return {Roo.Element} this
7045          */
7046         setVisibilityMode : function(visMode){
7047             this.visibilityMode = visMode;
7048             return this;
7049         },
7050         /**
7051          * Convenience method for setVisibilityMode(Element.DISPLAY)
7052          * @param {String} display (optional) What to set display to when visible
7053          * @return {Roo.Element} this
7054          */
7055         enableDisplayMode : function(display){
7056             this.setVisibilityMode(El.DISPLAY);
7057             if(typeof display != "undefined") this.originalDisplay = display;
7058             return this;
7059         },
7060
7061         /**
7062          * 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)
7063          * @param {String} selector The simple selector to test
7064          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7065                 search as a number or element (defaults to 10 || document.body)
7066          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7067          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7068          */
7069         findParent : function(simpleSelector, maxDepth, returnEl){
7070             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7071             maxDepth = maxDepth || 50;
7072             if(typeof maxDepth != "number"){
7073                 stopEl = Roo.getDom(maxDepth);
7074                 maxDepth = 10;
7075             }
7076             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7077                 if(dq.is(p, simpleSelector)){
7078                     return returnEl ? Roo.get(p) : p;
7079                 }
7080                 depth++;
7081                 p = p.parentNode;
7082             }
7083             return null;
7084         },
7085
7086
7087         /**
7088          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7089          * @param {String} selector The simple selector to test
7090          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7091                 search as a number or element (defaults to 10 || document.body)
7092          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7093          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7094          */
7095         findParentNode : function(simpleSelector, maxDepth, returnEl){
7096             var p = Roo.fly(this.dom.parentNode, '_internal');
7097             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7098         },
7099
7100         /**
7101          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7102          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7103          * @param {String} selector The simple selector to test
7104          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7105                 search as a number or element (defaults to 10 || document.body)
7106          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7107          */
7108         up : function(simpleSelector, maxDepth){
7109             return this.findParentNode(simpleSelector, maxDepth, true);
7110         },
7111
7112
7113
7114         /**
7115          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7116          * @param {String} selector The simple selector to test
7117          * @return {Boolean} True if this element matches the selector, else false
7118          */
7119         is : function(simpleSelector){
7120             return Roo.DomQuery.is(this.dom, simpleSelector);
7121         },
7122
7123         /**
7124          * Perform animation on this element.
7125          * @param {Object} args The YUI animation control args
7126          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7127          * @param {Function} onComplete (optional) Function to call when animation completes
7128          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7129          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7130          * @return {Roo.Element} this
7131          */
7132         animate : function(args, duration, onComplete, easing, animType){
7133             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7134             return this;
7135         },
7136
7137         /*
7138          * @private Internal animation call
7139          */
7140         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7141             animType = animType || 'run';
7142             opt = opt || {};
7143             var anim = Roo.lib.Anim[animType](
7144                 this.dom, args,
7145                 (opt.duration || defaultDur) || .35,
7146                 (opt.easing || defaultEase) || 'easeOut',
7147                 function(){
7148                     Roo.callback(cb, this);
7149                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7150                 },
7151                 this
7152             );
7153             opt.anim = anim;
7154             return anim;
7155         },
7156
7157         // private legacy anim prep
7158         preanim : function(a, i){
7159             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7160         },
7161
7162         /**
7163          * Removes worthless text nodes
7164          * @param {Boolean} forceReclean (optional) By default the element
7165          * keeps track if it has been cleaned already so
7166          * you can call this over and over. However, if you update the element and
7167          * need to force a reclean, you can pass true.
7168          */
7169         clean : function(forceReclean){
7170             if(this.isCleaned && forceReclean !== true){
7171                 return this;
7172             }
7173             var ns = /\S/;
7174             var d = this.dom, n = d.firstChild, ni = -1;
7175             while(n){
7176                 var nx = n.nextSibling;
7177                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7178                     d.removeChild(n);
7179                 }else{
7180                     n.nodeIndex = ++ni;
7181                 }
7182                 n = nx;
7183             }
7184             this.isCleaned = true;
7185             return this;
7186         },
7187
7188         // private
7189         calcOffsetsTo : function(el){
7190             el = Roo.get(el);
7191             var d = el.dom;
7192             var restorePos = false;
7193             if(el.getStyle('position') == 'static'){
7194                 el.position('relative');
7195                 restorePos = true;
7196             }
7197             var x = 0, y =0;
7198             var op = this.dom;
7199             while(op && op != d && op.tagName != 'HTML'){
7200                 x+= op.offsetLeft;
7201                 y+= op.offsetTop;
7202                 op = op.offsetParent;
7203             }
7204             if(restorePos){
7205                 el.position('static');
7206             }
7207             return [x, y];
7208         },
7209
7210         /**
7211          * Scrolls this element into view within the passed container.
7212          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7213          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7214          * @return {Roo.Element} this
7215          */
7216         scrollIntoView : function(container, hscroll){
7217             var c = Roo.getDom(container) || document.body;
7218             var el = this.dom;
7219
7220             var o = this.calcOffsetsTo(c),
7221                 l = o[0],
7222                 t = o[1],
7223                 b = t+el.offsetHeight,
7224                 r = l+el.offsetWidth;
7225
7226             var ch = c.clientHeight;
7227             var ct = parseInt(c.scrollTop, 10);
7228             var cl = parseInt(c.scrollLeft, 10);
7229             var cb = ct + ch;
7230             var cr = cl + c.clientWidth;
7231
7232             if(t < ct){
7233                 c.scrollTop = t;
7234             }else if(b > cb){
7235                 c.scrollTop = b-ch;
7236             }
7237
7238             if(hscroll !== false){
7239                 if(l < cl){
7240                     c.scrollLeft = l;
7241                 }else if(r > cr){
7242                     c.scrollLeft = r-c.clientWidth;
7243                 }
7244             }
7245             return this;
7246         },
7247
7248         // private
7249         scrollChildIntoView : function(child, hscroll){
7250             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7251         },
7252
7253         /**
7254          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7255          * the new height may not be available immediately.
7256          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7257          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7258          * @param {Function} onComplete (optional) Function to call when animation completes
7259          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7260          * @return {Roo.Element} this
7261          */
7262         autoHeight : function(animate, duration, onComplete, easing){
7263             var oldHeight = this.getHeight();
7264             this.clip();
7265             this.setHeight(1); // force clipping
7266             setTimeout(function(){
7267                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7268                 if(!animate){
7269                     this.setHeight(height);
7270                     this.unclip();
7271                     if(typeof onComplete == "function"){
7272                         onComplete();
7273                     }
7274                 }else{
7275                     this.setHeight(oldHeight); // restore original height
7276                     this.setHeight(height, animate, duration, function(){
7277                         this.unclip();
7278                         if(typeof onComplete == "function") onComplete();
7279                     }.createDelegate(this), easing);
7280                 }
7281             }.createDelegate(this), 0);
7282             return this;
7283         },
7284
7285         /**
7286          * Returns true if this element is an ancestor of the passed element
7287          * @param {HTMLElement/String} el The element to check
7288          * @return {Boolean} True if this element is an ancestor of el, else false
7289          */
7290         contains : function(el){
7291             if(!el){return false;}
7292             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7293         },
7294
7295         /**
7296          * Checks whether the element is currently visible using both visibility and display properties.
7297          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7298          * @return {Boolean} True if the element is currently visible, else false
7299          */
7300         isVisible : function(deep) {
7301             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7302             if(deep !== true || !vis){
7303                 return vis;
7304             }
7305             var p = this.dom.parentNode;
7306             while(p && p.tagName.toLowerCase() != "body"){
7307                 if(!Roo.fly(p, '_isVisible').isVisible()){
7308                     return false;
7309                 }
7310                 p = p.parentNode;
7311             }
7312             return true;
7313         },
7314
7315         /**
7316          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7317          * @param {String} selector The CSS selector
7318          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7319          * @return {CompositeElement/CompositeElementLite} The composite element
7320          */
7321         select : function(selector, unique){
7322             return El.select(selector, unique, this.dom);
7323         },
7324
7325         /**
7326          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7327          * @param {String} selector The CSS selector
7328          * @return {Array} An array of the matched nodes
7329          */
7330         query : function(selector, unique){
7331             return Roo.DomQuery.select(selector, this.dom);
7332         },
7333
7334         /**
7335          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7336          * @param {String} selector The CSS selector
7337          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7338          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7339          */
7340         child : function(selector, returnDom){
7341             var n = Roo.DomQuery.selectNode(selector, this.dom);
7342             return returnDom ? n : Roo.get(n);
7343         },
7344
7345         /**
7346          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7347          * @param {String} selector The CSS selector
7348          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7349          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7350          */
7351         down : function(selector, returnDom){
7352             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7353             return returnDom ? n : Roo.get(n);
7354         },
7355
7356         /**
7357          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7358          * @param {String} group The group the DD object is member of
7359          * @param {Object} config The DD config object
7360          * @param {Object} overrides An object containing methods to override/implement on the DD object
7361          * @return {Roo.dd.DD} The DD object
7362          */
7363         initDD : function(group, config, overrides){
7364             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7365             return Roo.apply(dd, overrides);
7366         },
7367
7368         /**
7369          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7370          * @param {String} group The group the DDProxy object is member of
7371          * @param {Object} config The DDProxy config object
7372          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7373          * @return {Roo.dd.DDProxy} The DDProxy object
7374          */
7375         initDDProxy : function(group, config, overrides){
7376             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7377             return Roo.apply(dd, overrides);
7378         },
7379
7380         /**
7381          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7382          * @param {String} group The group the DDTarget object is member of
7383          * @param {Object} config The DDTarget config object
7384          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7385          * @return {Roo.dd.DDTarget} The DDTarget object
7386          */
7387         initDDTarget : function(group, config, overrides){
7388             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7389             return Roo.apply(dd, overrides);
7390         },
7391
7392         /**
7393          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7394          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7395          * @param {Boolean} visible Whether the element is visible
7396          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7397          * @return {Roo.Element} this
7398          */
7399          setVisible : function(visible, animate){
7400             if(!animate || !A){
7401                 if(this.visibilityMode == El.DISPLAY){
7402                     this.setDisplayed(visible);
7403                 }else{
7404                     this.fixDisplay();
7405                     this.dom.style.visibility = visible ? "visible" : "hidden";
7406                 }
7407             }else{
7408                 // closure for composites
7409                 var dom = this.dom;
7410                 var visMode = this.visibilityMode;
7411                 if(visible){
7412                     this.setOpacity(.01);
7413                     this.setVisible(true);
7414                 }
7415                 this.anim({opacity: { to: (visible?1:0) }},
7416                       this.preanim(arguments, 1),
7417                       null, .35, 'easeIn', function(){
7418                          if(!visible){
7419                              if(visMode == El.DISPLAY){
7420                                  dom.style.display = "none";
7421                              }else{
7422                                  dom.style.visibility = "hidden";
7423                              }
7424                              Roo.get(dom).setOpacity(1);
7425                          }
7426                      });
7427             }
7428             return this;
7429         },
7430
7431         /**
7432          * Returns true if display is not "none"
7433          * @return {Boolean}
7434          */
7435         isDisplayed : function() {
7436             return this.getStyle("display") != "none";
7437         },
7438
7439         /**
7440          * Toggles the element's visibility or display, depending on visibility mode.
7441          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7442          * @return {Roo.Element} this
7443          */
7444         toggle : function(animate){
7445             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7446             return this;
7447         },
7448
7449         /**
7450          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7451          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7452          * @return {Roo.Element} this
7453          */
7454         setDisplayed : function(value) {
7455             if(typeof value == "boolean"){
7456                value = value ? this.originalDisplay : "none";
7457             }
7458             this.setStyle("display", value);
7459             return this;
7460         },
7461
7462         /**
7463          * Tries to focus the element. Any exceptions are caught and ignored.
7464          * @return {Roo.Element} this
7465          */
7466         focus : function() {
7467             try{
7468                 this.dom.focus();
7469             }catch(e){}
7470             return this;
7471         },
7472
7473         /**
7474          * Tries to blur the element. Any exceptions are caught and ignored.
7475          * @return {Roo.Element} this
7476          */
7477         blur : function() {
7478             try{
7479                 this.dom.blur();
7480             }catch(e){}
7481             return this;
7482         },
7483
7484         /**
7485          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7486          * @param {String/Array} className The CSS class to add, or an array of classes
7487          * @return {Roo.Element} this
7488          */
7489         addClass : function(className){
7490             if(className instanceof Array){
7491                 for(var i = 0, len = className.length; i < len; i++) {
7492                     this.addClass(className[i]);
7493                 }
7494             }else{
7495                 if(className && !this.hasClass(className)){
7496                     this.dom.className = this.dom.className + " " + className;
7497                 }
7498             }
7499             return this;
7500         },
7501
7502         /**
7503          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7504          * @param {String/Array} className The CSS class to add, or an array of classes
7505          * @return {Roo.Element} this
7506          */
7507         radioClass : function(className){
7508             var siblings = this.dom.parentNode.childNodes;
7509             for(var i = 0; i < siblings.length; i++) {
7510                 var s = siblings[i];
7511                 if(s.nodeType == 1){
7512                     Roo.get(s).removeClass(className);
7513                 }
7514             }
7515             this.addClass(className);
7516             return this;
7517         },
7518
7519         /**
7520          * Removes one or more CSS classes from the element.
7521          * @param {String/Array} className The CSS class to remove, or an array of classes
7522          * @return {Roo.Element} this
7523          */
7524         removeClass : function(className){
7525             if(!className || !this.dom.className){
7526                 return this;
7527             }
7528             if(className instanceof Array){
7529                 for(var i = 0, len = className.length; i < len; i++) {
7530                     this.removeClass(className[i]);
7531                 }
7532             }else{
7533                 if(this.hasClass(className)){
7534                     var re = this.classReCache[className];
7535                     if (!re) {
7536                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7537                        this.classReCache[className] = re;
7538                     }
7539                     this.dom.className =
7540                         this.dom.className.replace(re, " ");
7541                 }
7542             }
7543             return this;
7544         },
7545
7546         // private
7547         classReCache: {},
7548
7549         /**
7550          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7551          * @param {String} className The CSS class to toggle
7552          * @return {Roo.Element} this
7553          */
7554         toggleClass : function(className){
7555             if(this.hasClass(className)){
7556                 this.removeClass(className);
7557             }else{
7558                 this.addClass(className);
7559             }
7560             return this;
7561         },
7562
7563         /**
7564          * Checks if the specified CSS class exists on this element's DOM node.
7565          * @param {String} className The CSS class to check for
7566          * @return {Boolean} True if the class exists, else false
7567          */
7568         hasClass : function(className){
7569             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7570         },
7571
7572         /**
7573          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7574          * @param {String} oldClassName The CSS class to replace
7575          * @param {String} newClassName The replacement CSS class
7576          * @return {Roo.Element} this
7577          */
7578         replaceClass : function(oldClassName, newClassName){
7579             this.removeClass(oldClassName);
7580             this.addClass(newClassName);
7581             return this;
7582         },
7583
7584         /**
7585          * Returns an object with properties matching the styles requested.
7586          * For example, el.getStyles('color', 'font-size', 'width') might return
7587          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7588          * @param {String} style1 A style name
7589          * @param {String} style2 A style name
7590          * @param {String} etc.
7591          * @return {Object} The style object
7592          */
7593         getStyles : function(){
7594             var a = arguments, len = a.length, r = {};
7595             for(var i = 0; i < len; i++){
7596                 r[a[i]] = this.getStyle(a[i]);
7597             }
7598             return r;
7599         },
7600
7601         /**
7602          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7603          * @param {String} property The style property whose value is returned.
7604          * @return {String} The current value of the style property for this element.
7605          */
7606         getStyle : function(){
7607             return view && view.getComputedStyle ?
7608                 function(prop){
7609                     var el = this.dom, v, cs, camel;
7610                     if(prop == 'float'){
7611                         prop = "cssFloat";
7612                     }
7613                     if(el.style && (v = el.style[prop])){
7614                         return v;
7615                     }
7616                     if(cs = view.getComputedStyle(el, "")){
7617                         if(!(camel = propCache[prop])){
7618                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7619                         }
7620                         return cs[camel];
7621                     }
7622                     return null;
7623                 } :
7624                 function(prop){
7625                     var el = this.dom, v, cs, camel;
7626                     if(prop == 'opacity'){
7627                         if(typeof el.style.filter == 'string'){
7628                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7629                             if(m){
7630                                 var fv = parseFloat(m[1]);
7631                                 if(!isNaN(fv)){
7632                                     return fv ? fv / 100 : 0;
7633                                 }
7634                             }
7635                         }
7636                         return 1;
7637                     }else if(prop == 'float'){
7638                         prop = "styleFloat";
7639                     }
7640                     if(!(camel = propCache[prop])){
7641                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7642                     }
7643                     if(v = el.style[camel]){
7644                         return v;
7645                     }
7646                     if(cs = el.currentStyle){
7647                         return cs[camel];
7648                     }
7649                     return null;
7650                 };
7651         }(),
7652
7653         /**
7654          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7655          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7656          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7657          * @return {Roo.Element} this
7658          */
7659         setStyle : function(prop, value){
7660             if(typeof prop == "string"){
7661                 
7662                 if (prop == 'float') {
7663                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7664                     return this;
7665                 }
7666                 
7667                 var camel;
7668                 if(!(camel = propCache[prop])){
7669                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7670                 }
7671                 
7672                 if(camel == 'opacity') {
7673                     this.setOpacity(value);
7674                 }else{
7675                     this.dom.style[camel] = value;
7676                 }
7677             }else{
7678                 for(var style in prop){
7679                     if(typeof prop[style] != "function"){
7680                        this.setStyle(style, prop[style]);
7681                     }
7682                 }
7683             }
7684             return this;
7685         },
7686
7687         /**
7688          * More flexible version of {@link #setStyle} for setting style properties.
7689          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7690          * a function which returns such a specification.
7691          * @return {Roo.Element} this
7692          */
7693         applyStyles : function(style){
7694             Roo.DomHelper.applyStyles(this.dom, style);
7695             return this;
7696         },
7697
7698         /**
7699           * 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).
7700           * @return {Number} The X position of the element
7701           */
7702         getX : function(){
7703             return D.getX(this.dom);
7704         },
7705
7706         /**
7707           * 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).
7708           * @return {Number} The Y position of the element
7709           */
7710         getY : function(){
7711             return D.getY(this.dom);
7712         },
7713
7714         /**
7715           * 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).
7716           * @return {Array} The XY position of the element
7717           */
7718         getXY : function(){
7719             return D.getXY(this.dom);
7720         },
7721
7722         /**
7723          * 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).
7724          * @param {Number} The X position of the element
7725          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7726          * @return {Roo.Element} this
7727          */
7728         setX : function(x, animate){
7729             if(!animate || !A){
7730                 D.setX(this.dom, x);
7731             }else{
7732                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7733             }
7734             return this;
7735         },
7736
7737         /**
7738          * 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).
7739          * @param {Number} The Y position of the element
7740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7741          * @return {Roo.Element} this
7742          */
7743         setY : function(y, animate){
7744             if(!animate || !A){
7745                 D.setY(this.dom, y);
7746             }else{
7747                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7754          * @param {String} left The left CSS property value
7755          * @return {Roo.Element} this
7756          */
7757         setLeft : function(left){
7758             this.setStyle("left", this.addUnits(left));
7759             return this;
7760         },
7761
7762         /**
7763          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7764          * @param {String} top The top CSS property value
7765          * @return {Roo.Element} this
7766          */
7767         setTop : function(top){
7768             this.setStyle("top", this.addUnits(top));
7769             return this;
7770         },
7771
7772         /**
7773          * Sets the element's CSS right style.
7774          * @param {String} right The right CSS property value
7775          * @return {Roo.Element} this
7776          */
7777         setRight : function(right){
7778             this.setStyle("right", this.addUnits(right));
7779             return this;
7780         },
7781
7782         /**
7783          * Sets the element's CSS bottom style.
7784          * @param {String} bottom The bottom CSS property value
7785          * @return {Roo.Element} this
7786          */
7787         setBottom : function(bottom){
7788             this.setStyle("bottom", this.addUnits(bottom));
7789             return this;
7790         },
7791
7792         /**
7793          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7794          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7795          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7796          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7797          * @return {Roo.Element} this
7798          */
7799         setXY : function(pos, animate){
7800             if(!animate || !A){
7801                 D.setXY(this.dom, pos);
7802             }else{
7803                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7804             }
7805             return this;
7806         },
7807
7808         /**
7809          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7810          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7811          * @param {Number} x X value for new position (coordinates are page-based)
7812          * @param {Number} y Y value for new position (coordinates are page-based)
7813          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7814          * @return {Roo.Element} this
7815          */
7816         setLocation : function(x, y, animate){
7817             this.setXY([x, y], this.preanim(arguments, 2));
7818             return this;
7819         },
7820
7821         /**
7822          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7823          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7824          * @param {Number} x X value for new position (coordinates are page-based)
7825          * @param {Number} y Y value for new position (coordinates are page-based)
7826          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7827          * @return {Roo.Element} this
7828          */
7829         moveTo : function(x, y, animate){
7830             this.setXY([x, y], this.preanim(arguments, 2));
7831             return this;
7832         },
7833
7834         /**
7835          * Returns the region of the given element.
7836          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7837          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7838          */
7839         getRegion : function(){
7840             return D.getRegion(this.dom);
7841         },
7842
7843         /**
7844          * Returns the offset height of the element
7845          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7846          * @return {Number} The element's height
7847          */
7848         getHeight : function(contentHeight){
7849             var h = this.dom.offsetHeight || 0;
7850             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7851         },
7852
7853         /**
7854          * Returns the offset width of the element
7855          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7856          * @return {Number} The element's width
7857          */
7858         getWidth : function(contentWidth){
7859             var w = this.dom.offsetWidth || 0;
7860             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7861         },
7862
7863         /**
7864          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7865          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7866          * if a height has not been set using CSS.
7867          * @return {Number}
7868          */
7869         getComputedHeight : function(){
7870             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7871             if(!h){
7872                 h = parseInt(this.getStyle('height'), 10) || 0;
7873                 if(!this.isBorderBox()){
7874                     h += this.getFrameWidth('tb');
7875                 }
7876             }
7877             return h;
7878         },
7879
7880         /**
7881          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7882          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7883          * if a width has not been set using CSS.
7884          * @return {Number}
7885          */
7886         getComputedWidth : function(){
7887             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7888             if(!w){
7889                 w = parseInt(this.getStyle('width'), 10) || 0;
7890                 if(!this.isBorderBox()){
7891                     w += this.getFrameWidth('lr');
7892                 }
7893             }
7894             return w;
7895         },
7896
7897         /**
7898          * Returns the size of the element.
7899          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7900          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7901          */
7902         getSize : function(contentSize){
7903             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7904         },
7905
7906         /**
7907          * Returns the width and height of the viewport.
7908          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7909          */
7910         getViewSize : function(){
7911             var d = this.dom, doc = document, aw = 0, ah = 0;
7912             if(d == doc || d == doc.body){
7913                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7914             }else{
7915                 return {
7916                     width : d.clientWidth,
7917                     height: d.clientHeight
7918                 };
7919             }
7920         },
7921
7922         /**
7923          * Returns the value of the "value" attribute
7924          * @param {Boolean} asNumber true to parse the value as a number
7925          * @return {String/Number}
7926          */
7927         getValue : function(asNumber){
7928             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7929         },
7930
7931         // private
7932         adjustWidth : function(width){
7933             if(typeof width == "number"){
7934                 if(this.autoBoxAdjust && !this.isBorderBox()){
7935                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7936                 }
7937                 if(width < 0){
7938                     width = 0;
7939                 }
7940             }
7941             return width;
7942         },
7943
7944         // private
7945         adjustHeight : function(height){
7946             if(typeof height == "number"){
7947                if(this.autoBoxAdjust && !this.isBorderBox()){
7948                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7949                }
7950                if(height < 0){
7951                    height = 0;
7952                }
7953             }
7954             return height;
7955         },
7956
7957         /**
7958          * Set the width of the element
7959          * @param {Number} width The new width
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963         setWidth : function(width, animate){
7964             width = this.adjustWidth(width);
7965             if(!animate || !A){
7966                 this.dom.style.width = this.addUnits(width);
7967             }else{
7968                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7969             }
7970             return this;
7971         },
7972
7973         /**
7974          * Set the height of the element
7975          * @param {Number} height The new height
7976          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7977          * @return {Roo.Element} this
7978          */
7979          setHeight : function(height, animate){
7980             height = this.adjustHeight(height);
7981             if(!animate || !A){
7982                 this.dom.style.height = this.addUnits(height);
7983             }else{
7984                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7985             }
7986             return this;
7987         },
7988
7989         /**
7990          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7991          * @param {Number} width The new width
7992          * @param {Number} height The new height
7993          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7994          * @return {Roo.Element} this
7995          */
7996          setSize : function(width, height, animate){
7997             if(typeof width == "object"){ // in case of object from getSize()
7998                 height = width.height; width = width.width;
7999             }
8000             width = this.adjustWidth(width); height = this.adjustHeight(height);
8001             if(!animate || !A){
8002                 this.dom.style.width = this.addUnits(width);
8003                 this.dom.style.height = this.addUnits(height);
8004             }else{
8005                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8006             }
8007             return this;
8008         },
8009
8010         /**
8011          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8012          * @param {Number} x X value for new position (coordinates are page-based)
8013          * @param {Number} y Y value for new position (coordinates are page-based)
8014          * @param {Number} width The new width
8015          * @param {Number} height The new height
8016          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8017          * @return {Roo.Element} this
8018          */
8019         setBounds : function(x, y, width, height, animate){
8020             if(!animate || !A){
8021                 this.setSize(width, height);
8022                 this.setLocation(x, y);
8023             }else{
8024                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8025                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8026                               this.preanim(arguments, 4), 'motion');
8027             }
8028             return this;
8029         },
8030
8031         /**
8032          * 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.
8033          * @param {Roo.lib.Region} region The region to fill
8034          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8035          * @return {Roo.Element} this
8036          */
8037         setRegion : function(region, animate){
8038             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8039             return this;
8040         },
8041
8042         /**
8043          * Appends an event handler
8044          *
8045          * @param {String}   eventName     The type of event to append
8046          * @param {Function} fn        The method the event invokes
8047          * @param {Object} scope       (optional) The scope (this object) of the fn
8048          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8049          */
8050         addListener : function(eventName, fn, scope, options){
8051             if (this.dom) {
8052                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8053             }
8054         },
8055
8056         /**
8057          * Removes an event handler from this element
8058          * @param {String} eventName the type of event to remove
8059          * @param {Function} fn the method the event invokes
8060          * @return {Roo.Element} this
8061          */
8062         removeListener : function(eventName, fn){
8063             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8064             return this;
8065         },
8066
8067         /**
8068          * Removes all previous added listeners from this element
8069          * @return {Roo.Element} this
8070          */
8071         removeAllListeners : function(){
8072             E.purgeElement(this.dom);
8073             return this;
8074         },
8075
8076         relayEvent : function(eventName, observable){
8077             this.on(eventName, function(e){
8078                 observable.fireEvent(eventName, e);
8079             });
8080         },
8081
8082         /**
8083          * Set the opacity of the element
8084          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8085          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8086          * @return {Roo.Element} this
8087          */
8088          setOpacity : function(opacity, animate){
8089             if(!animate || !A){
8090                 var s = this.dom.style;
8091                 if(Roo.isIE){
8092                     s.zoom = 1;
8093                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8094                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8095                 }else{
8096                     s.opacity = opacity;
8097                 }
8098             }else{
8099                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8100             }
8101             return this;
8102         },
8103
8104         /**
8105          * Gets the left X coordinate
8106          * @param {Boolean} local True to get the local css position instead of page coordinate
8107          * @return {Number}
8108          */
8109         getLeft : function(local){
8110             if(!local){
8111                 return this.getX();
8112             }else{
8113                 return parseInt(this.getStyle("left"), 10) || 0;
8114             }
8115         },
8116
8117         /**
8118          * Gets the right X coordinate of the element (element X position + element width)
8119          * @param {Boolean} local True to get the local css position instead of page coordinate
8120          * @return {Number}
8121          */
8122         getRight : function(local){
8123             if(!local){
8124                 return this.getX() + this.getWidth();
8125             }else{
8126                 return (this.getLeft(true) + this.getWidth()) || 0;
8127             }
8128         },
8129
8130         /**
8131          * Gets the top Y coordinate
8132          * @param {Boolean} local True to get the local css position instead of page coordinate
8133          * @return {Number}
8134          */
8135         getTop : function(local) {
8136             if(!local){
8137                 return this.getY();
8138             }else{
8139                 return parseInt(this.getStyle("top"), 10) || 0;
8140             }
8141         },
8142
8143         /**
8144          * Gets the bottom Y coordinate of the element (element Y position + element height)
8145          * @param {Boolean} local True to get the local css position instead of page coordinate
8146          * @return {Number}
8147          */
8148         getBottom : function(local){
8149             if(!local){
8150                 return this.getY() + this.getHeight();
8151             }else{
8152                 return (this.getTop(true) + this.getHeight()) || 0;
8153             }
8154         },
8155
8156         /**
8157         * Initializes positioning on this element. If a desired position is not passed, it will make the
8158         * the element positioned relative IF it is not already positioned.
8159         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8160         * @param {Number} zIndex (optional) The zIndex to apply
8161         * @param {Number} x (optional) Set the page X position
8162         * @param {Number} y (optional) Set the page Y position
8163         */
8164         position : function(pos, zIndex, x, y){
8165             if(!pos){
8166                if(this.getStyle('position') == 'static'){
8167                    this.setStyle('position', 'relative');
8168                }
8169             }else{
8170                 this.setStyle("position", pos);
8171             }
8172             if(zIndex){
8173                 this.setStyle("z-index", zIndex);
8174             }
8175             if(x !== undefined && y !== undefined){
8176                 this.setXY([x, y]);
8177             }else if(x !== undefined){
8178                 this.setX(x);
8179             }else if(y !== undefined){
8180                 this.setY(y);
8181             }
8182         },
8183
8184         /**
8185         * Clear positioning back to the default when the document was loaded
8186         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8187         * @return {Roo.Element} this
8188          */
8189         clearPositioning : function(value){
8190             value = value ||'';
8191             this.setStyle({
8192                 "left": value,
8193                 "right": value,
8194                 "top": value,
8195                 "bottom": value,
8196                 "z-index": "",
8197                 "position" : "static"
8198             });
8199             return this;
8200         },
8201
8202         /**
8203         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8204         * snapshot before performing an update and then restoring the element.
8205         * @return {Object}
8206         */
8207         getPositioning : function(){
8208             var l = this.getStyle("left");
8209             var t = this.getStyle("top");
8210             return {
8211                 "position" : this.getStyle("position"),
8212                 "left" : l,
8213                 "right" : l ? "" : this.getStyle("right"),
8214                 "top" : t,
8215                 "bottom" : t ? "" : this.getStyle("bottom"),
8216                 "z-index" : this.getStyle("z-index")
8217             };
8218         },
8219
8220         /**
8221          * Gets the width of the border(s) for the specified side(s)
8222          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8223          * passing lr would get the border (l)eft width + the border (r)ight width.
8224          * @return {Number} The width of the sides passed added together
8225          */
8226         getBorderWidth : function(side){
8227             return this.addStyles(side, El.borders);
8228         },
8229
8230         /**
8231          * Gets the width of the padding(s) for the specified side(s)
8232          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8233          * passing lr would get the padding (l)eft + the padding (r)ight.
8234          * @return {Number} The padding of the sides passed added together
8235          */
8236         getPadding : function(side){
8237             return this.addStyles(side, El.paddings);
8238         },
8239
8240         /**
8241         * Set positioning with an object returned by getPositioning().
8242         * @param {Object} posCfg
8243         * @return {Roo.Element} this
8244          */
8245         setPositioning : function(pc){
8246             this.applyStyles(pc);
8247             if(pc.right == "auto"){
8248                 this.dom.style.right = "";
8249             }
8250             if(pc.bottom == "auto"){
8251                 this.dom.style.bottom = "";
8252             }
8253             return this;
8254         },
8255
8256         // private
8257         fixDisplay : function(){
8258             if(this.getStyle("display") == "none"){
8259                 this.setStyle("visibility", "hidden");
8260                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8261                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8262                     this.setStyle("display", "block");
8263                 }
8264             }
8265         },
8266
8267         /**
8268          * Quick set left and top adding default units
8269          * @param {String} left The left CSS property value
8270          * @param {String} top The top CSS property value
8271          * @return {Roo.Element} this
8272          */
8273          setLeftTop : function(left, top){
8274             this.dom.style.left = this.addUnits(left);
8275             this.dom.style.top = this.addUnits(top);
8276             return this;
8277         },
8278
8279         /**
8280          * Move this element relative to its current position.
8281          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8282          * @param {Number} distance How far to move the element in pixels
8283          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8284          * @return {Roo.Element} this
8285          */
8286          move : function(direction, distance, animate){
8287             var xy = this.getXY();
8288             direction = direction.toLowerCase();
8289             switch(direction){
8290                 case "l":
8291                 case "left":
8292                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8293                     break;
8294                case "r":
8295                case "right":
8296                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8297                     break;
8298                case "t":
8299                case "top":
8300                case "up":
8301                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8302                     break;
8303                case "b":
8304                case "bottom":
8305                case "down":
8306                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8307                     break;
8308             }
8309             return this;
8310         },
8311
8312         /**
8313          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8314          * @return {Roo.Element} this
8315          */
8316         clip : function(){
8317             if(!this.isClipped){
8318                this.isClipped = true;
8319                this.originalClip = {
8320                    "o": this.getStyle("overflow"),
8321                    "x": this.getStyle("overflow-x"),
8322                    "y": this.getStyle("overflow-y")
8323                };
8324                this.setStyle("overflow", "hidden");
8325                this.setStyle("overflow-x", "hidden");
8326                this.setStyle("overflow-y", "hidden");
8327             }
8328             return this;
8329         },
8330
8331         /**
8332          *  Return clipping (overflow) to original clipping before clip() was called
8333          * @return {Roo.Element} this
8334          */
8335         unclip : function(){
8336             if(this.isClipped){
8337                 this.isClipped = false;
8338                 var o = this.originalClip;
8339                 if(o.o){this.setStyle("overflow", o.o);}
8340                 if(o.x){this.setStyle("overflow-x", o.x);}
8341                 if(o.y){this.setStyle("overflow-y", o.y);}
8342             }
8343             return this;
8344         },
8345
8346
8347         /**
8348          * Gets the x,y coordinates specified by the anchor position on the element.
8349          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8350          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8351          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8352          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8353          * @return {Array} [x, y] An array containing the element's x and y coordinates
8354          */
8355         getAnchorXY : function(anchor, local, s){
8356             //Passing a different size is useful for pre-calculating anchors,
8357             //especially for anchored animations that change the el size.
8358
8359             var w, h, vp = false;
8360             if(!s){
8361                 var d = this.dom;
8362                 if(d == document.body || d == document){
8363                     vp = true;
8364                     w = D.getViewWidth(); h = D.getViewHeight();
8365                 }else{
8366                     w = this.getWidth(); h = this.getHeight();
8367                 }
8368             }else{
8369                 w = s.width;  h = s.height;
8370             }
8371             var x = 0, y = 0, r = Math.round;
8372             switch((anchor || "tl").toLowerCase()){
8373                 case "c":
8374                     x = r(w*.5);
8375                     y = r(h*.5);
8376                 break;
8377                 case "t":
8378                     x = r(w*.5);
8379                     y = 0;
8380                 break;
8381                 case "l":
8382                     x = 0;
8383                     y = r(h*.5);
8384                 break;
8385                 case "r":
8386                     x = w;
8387                     y = r(h*.5);
8388                 break;
8389                 case "b":
8390                     x = r(w*.5);
8391                     y = h;
8392                 break;
8393                 case "tl":
8394                     x = 0;
8395                     y = 0;
8396                 break;
8397                 case "bl":
8398                     x = 0;
8399                     y = h;
8400                 break;
8401                 case "br":
8402                     x = w;
8403                     y = h;
8404                 break;
8405                 case "tr":
8406                     x = w;
8407                     y = 0;
8408                 break;
8409             }
8410             if(local === true){
8411                 return [x, y];
8412             }
8413             if(vp){
8414                 var sc = this.getScroll();
8415                 return [x + sc.left, y + sc.top];
8416             }
8417             //Add the element's offset xy
8418             var o = this.getXY();
8419             return [x+o[0], y+o[1]];
8420         },
8421
8422         /**
8423          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8424          * supported position values.
8425          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8426          * @param {String} position The position to align to.
8427          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8428          * @return {Array} [x, y]
8429          */
8430         getAlignToXY : function(el, p, o){
8431             el = Roo.get(el);
8432             var d = this.dom;
8433             if(!el.dom){
8434                 throw "Element.alignTo with an element that doesn't exist";
8435             }
8436             var c = false; //constrain to viewport
8437             var p1 = "", p2 = "";
8438             o = o || [0,0];
8439
8440             if(!p){
8441                 p = "tl-bl";
8442             }else if(p == "?"){
8443                 p = "tl-bl?";
8444             }else if(p.indexOf("-") == -1){
8445                 p = "tl-" + p;
8446             }
8447             p = p.toLowerCase();
8448             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8449             if(!m){
8450                throw "Element.alignTo with an invalid alignment " + p;
8451             }
8452             p1 = m[1]; p2 = m[2]; c = !!m[3];
8453
8454             //Subtract the aligned el's internal xy from the target's offset xy
8455             //plus custom offset to get the aligned el's new offset xy
8456             var a1 = this.getAnchorXY(p1, true);
8457             var a2 = el.getAnchorXY(p2, false);
8458             var x = a2[0] - a1[0] + o[0];
8459             var y = a2[1] - a1[1] + o[1];
8460             if(c){
8461                 //constrain the aligned el to viewport if necessary
8462                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8463                 // 5px of margin for ie
8464                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8465
8466                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8467                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8468                 //otherwise swap the aligned el to the opposite border of the target.
8469                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8470                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8471                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8472                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8473
8474                var doc = document;
8475                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8476                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8477
8478                if((x+w) > dw + scrollX){
8479                     x = swapX ? r.left-w : dw+scrollX-w;
8480                 }
8481                if(x < scrollX){
8482                    x = swapX ? r.right : scrollX;
8483                }
8484                if((y+h) > dh + scrollY){
8485                     y = swapY ? r.top-h : dh+scrollY-h;
8486                 }
8487                if (y < scrollY){
8488                    y = swapY ? r.bottom : scrollY;
8489                }
8490             }
8491             return [x,y];
8492         },
8493
8494         // private
8495         getConstrainToXY : function(){
8496             var os = {top:0, left:0, bottom:0, right: 0};
8497
8498             return function(el, local, offsets, proposedXY){
8499                 el = Roo.get(el);
8500                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8501
8502                 var vw, vh, vx = 0, vy = 0;
8503                 if(el.dom == document.body || el.dom == document){
8504                     vw = Roo.lib.Dom.getViewWidth();
8505                     vh = Roo.lib.Dom.getViewHeight();
8506                 }else{
8507                     vw = el.dom.clientWidth;
8508                     vh = el.dom.clientHeight;
8509                     if(!local){
8510                         var vxy = el.getXY();
8511                         vx = vxy[0];
8512                         vy = vxy[1];
8513                     }
8514                 }
8515
8516                 var s = el.getScroll();
8517
8518                 vx += offsets.left + s.left;
8519                 vy += offsets.top + s.top;
8520
8521                 vw -= offsets.right;
8522                 vh -= offsets.bottom;
8523
8524                 var vr = vx+vw;
8525                 var vb = vy+vh;
8526
8527                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8528                 var x = xy[0], y = xy[1];
8529                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8530
8531                 // only move it if it needs it
8532                 var moved = false;
8533
8534                 // first validate right/bottom
8535                 if((x + w) > vr){
8536                     x = vr - w;
8537                     moved = true;
8538                 }
8539                 if((y + h) > vb){
8540                     y = vb - h;
8541                     moved = true;
8542                 }
8543                 // then make sure top/left isn't negative
8544                 if(x < vx){
8545                     x = vx;
8546                     moved = true;
8547                 }
8548                 if(y < vy){
8549                     y = vy;
8550                     moved = true;
8551                 }
8552                 return moved ? [x, y] : false;
8553             };
8554         }(),
8555
8556         // private
8557         adjustForConstraints : function(xy, parent, offsets){
8558             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8559         },
8560
8561         /**
8562          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8563          * document it aligns it to the viewport.
8564          * The position parameter is optional, and can be specified in any one of the following formats:
8565          * <ul>
8566          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8567          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8568          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8569          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8570          *   <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
8571          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8572          * </ul>
8573          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8574          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8575          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8576          * that specified in order to enforce the viewport constraints.
8577          * Following are all of the supported anchor positions:
8578     <pre>
8579     Value  Description
8580     -----  -----------------------------
8581     tl     The top left corner (default)
8582     t      The center of the top edge
8583     tr     The top right corner
8584     l      The center of the left edge
8585     c      In the center of the element
8586     r      The center of the right edge
8587     bl     The bottom left corner
8588     b      The center of the bottom edge
8589     br     The bottom right corner
8590     </pre>
8591     Example Usage:
8592     <pre><code>
8593     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8594     el.alignTo("other-el");
8595
8596     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8597     el.alignTo("other-el", "tr?");
8598
8599     // align the bottom right corner of el with the center left edge of other-el
8600     el.alignTo("other-el", "br-l?");
8601
8602     // align the center of el with the bottom left corner of other-el and
8603     // adjust the x position by -6 pixels (and the y position by 0)
8604     el.alignTo("other-el", "c-bl", [-6, 0]);
8605     </code></pre>
8606          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8607          * @param {String} position The position to align to.
8608          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8609          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8610          * @return {Roo.Element} this
8611          */
8612         alignTo : function(element, position, offsets, animate){
8613             var xy = this.getAlignToXY(element, position, offsets);
8614             this.setXY(xy, this.preanim(arguments, 3));
8615             return this;
8616         },
8617
8618         /**
8619          * Anchors an element to another element and realigns it when the window is resized.
8620          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8621          * @param {String} position The position to align to.
8622          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8623          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8624          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8625          * is a number, it is used as the buffer delay (defaults to 50ms).
8626          * @param {Function} callback The function to call after the animation finishes
8627          * @return {Roo.Element} this
8628          */
8629         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8630             var action = function(){
8631                 this.alignTo(el, alignment, offsets, animate);
8632                 Roo.callback(callback, this);
8633             };
8634             Roo.EventManager.onWindowResize(action, this);
8635             var tm = typeof monitorScroll;
8636             if(tm != 'undefined'){
8637                 Roo.EventManager.on(window, 'scroll', action, this,
8638                     {buffer: tm == 'number' ? monitorScroll : 50});
8639             }
8640             action.call(this); // align immediately
8641             return this;
8642         },
8643         /**
8644          * Clears any opacity settings from this element. Required in some cases for IE.
8645          * @return {Roo.Element} this
8646          */
8647         clearOpacity : function(){
8648             if (window.ActiveXObject) {
8649                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8650                     this.dom.style.filter = "";
8651                 }
8652             } else {
8653                 this.dom.style.opacity = "";
8654                 this.dom.style["-moz-opacity"] = "";
8655                 this.dom.style["-khtml-opacity"] = "";
8656             }
8657             return this;
8658         },
8659
8660         /**
8661          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8662          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8663          * @return {Roo.Element} this
8664          */
8665         hide : function(animate){
8666             this.setVisible(false, this.preanim(arguments, 0));
8667             return this;
8668         },
8669
8670         /**
8671         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8672         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8673          * @return {Roo.Element} this
8674          */
8675         show : function(animate){
8676             this.setVisible(true, this.preanim(arguments, 0));
8677             return this;
8678         },
8679
8680         /**
8681          * @private Test if size has a unit, otherwise appends the default
8682          */
8683         addUnits : function(size){
8684             return Roo.Element.addUnits(size, this.defaultUnit);
8685         },
8686
8687         /**
8688          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8689          * @return {Roo.Element} this
8690          */
8691         beginMeasure : function(){
8692             var el = this.dom;
8693             if(el.offsetWidth || el.offsetHeight){
8694                 return this; // offsets work already
8695             }
8696             var changed = [];
8697             var p = this.dom, b = document.body; // start with this element
8698             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8699                 var pe = Roo.get(p);
8700                 if(pe.getStyle('display') == 'none'){
8701                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8702                     p.style.visibility = "hidden";
8703                     p.style.display = "block";
8704                 }
8705                 p = p.parentNode;
8706             }
8707             this._measureChanged = changed;
8708             return this;
8709
8710         },
8711
8712         /**
8713          * Restores displays to before beginMeasure was called
8714          * @return {Roo.Element} this
8715          */
8716         endMeasure : function(){
8717             var changed = this._measureChanged;
8718             if(changed){
8719                 for(var i = 0, len = changed.length; i < len; i++) {
8720                     var r = changed[i];
8721                     r.el.style.visibility = r.visibility;
8722                     r.el.style.display = "none";
8723                 }
8724                 this._measureChanged = null;
8725             }
8726             return this;
8727         },
8728
8729         /**
8730         * Update the innerHTML of this element, optionally searching for and processing scripts
8731         * @param {String} html The new HTML
8732         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8733         * @param {Function} callback For async script loading you can be noticed when the update completes
8734         * @return {Roo.Element} this
8735          */
8736         update : function(html, loadScripts, callback){
8737             if(typeof html == "undefined"){
8738                 html = "";
8739             }
8740             if(loadScripts !== true){
8741                 this.dom.innerHTML = html;
8742                 if(typeof callback == "function"){
8743                     callback();
8744                 }
8745                 return this;
8746             }
8747             var id = Roo.id();
8748             var dom = this.dom;
8749
8750             html += '<span id="' + id + '"></span>';
8751
8752             E.onAvailable(id, function(){
8753                 var hd = document.getElementsByTagName("head")[0];
8754                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8755                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8756                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8757
8758                 var match;
8759                 while(match = re.exec(html)){
8760                     var attrs = match[1];
8761                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8762                     if(srcMatch && srcMatch[2]){
8763                        var s = document.createElement("script");
8764                        s.src = srcMatch[2];
8765                        var typeMatch = attrs.match(typeRe);
8766                        if(typeMatch && typeMatch[2]){
8767                            s.type = typeMatch[2];
8768                        }
8769                        hd.appendChild(s);
8770                     }else if(match[2] && match[2].length > 0){
8771                         if(window.execScript) {
8772                            window.execScript(match[2]);
8773                         } else {
8774                             /**
8775                              * eval:var:id
8776                              * eval:var:dom
8777                              * eval:var:html
8778                              * 
8779                              */
8780                            window.eval(match[2]);
8781                         }
8782                     }
8783                 }
8784                 var el = document.getElementById(id);
8785                 if(el){el.parentNode.removeChild(el);}
8786                 if(typeof callback == "function"){
8787                     callback();
8788                 }
8789             });
8790             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8791             return this;
8792         },
8793
8794         /**
8795          * Direct access to the UpdateManager update() method (takes the same parameters).
8796          * @param {String/Function} url The url for this request or a function to call to get the url
8797          * @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}
8798          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8799          * @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.
8800          * @return {Roo.Element} this
8801          */
8802         load : function(){
8803             var um = this.getUpdateManager();
8804             um.update.apply(um, arguments);
8805             return this;
8806         },
8807
8808         /**
8809         * Gets this element's UpdateManager
8810         * @return {Roo.UpdateManager} The UpdateManager
8811         */
8812         getUpdateManager : function(){
8813             if(!this.updateManager){
8814                 this.updateManager = new Roo.UpdateManager(this);
8815             }
8816             return this.updateManager;
8817         },
8818
8819         /**
8820          * Disables text selection for this element (normalized across browsers)
8821          * @return {Roo.Element} this
8822          */
8823         unselectable : function(){
8824             this.dom.unselectable = "on";
8825             this.swallowEvent("selectstart", true);
8826             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8827             this.addClass("x-unselectable");
8828             return this;
8829         },
8830
8831         /**
8832         * Calculates the x, y to center this element on the screen
8833         * @return {Array} The x, y values [x, y]
8834         */
8835         getCenterXY : function(){
8836             return this.getAlignToXY(document, 'c-c');
8837         },
8838
8839         /**
8840         * Centers the Element in either the viewport, or another Element.
8841         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8842         */
8843         center : function(centerIn){
8844             this.alignTo(centerIn || document, 'c-c');
8845             return this;
8846         },
8847
8848         /**
8849          * Tests various css rules/browsers to determine if this element uses a border box
8850          * @return {Boolean}
8851          */
8852         isBorderBox : function(){
8853             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8854         },
8855
8856         /**
8857          * Return a box {x, y, width, height} that can be used to set another elements
8858          * size/location to match this element.
8859          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8860          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8861          * @return {Object} box An object in the format {x, y, width, height}
8862          */
8863         getBox : function(contentBox, local){
8864             var xy;
8865             if(!local){
8866                 xy = this.getXY();
8867             }else{
8868                 var left = parseInt(this.getStyle("left"), 10) || 0;
8869                 var top = parseInt(this.getStyle("top"), 10) || 0;
8870                 xy = [left, top];
8871             }
8872             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8873             if(!contentBox){
8874                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8875             }else{
8876                 var l = this.getBorderWidth("l")+this.getPadding("l");
8877                 var r = this.getBorderWidth("r")+this.getPadding("r");
8878                 var t = this.getBorderWidth("t")+this.getPadding("t");
8879                 var b = this.getBorderWidth("b")+this.getPadding("b");
8880                 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)};
8881             }
8882             bx.right = bx.x + bx.width;
8883             bx.bottom = bx.y + bx.height;
8884             return bx;
8885         },
8886
8887         /**
8888          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8889          for more information about the sides.
8890          * @param {String} sides
8891          * @return {Number}
8892          */
8893         getFrameWidth : function(sides, onlyContentBox){
8894             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8895         },
8896
8897         /**
8898          * 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.
8899          * @param {Object} box The box to fill {x, y, width, height}
8900          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8901          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8902          * @return {Roo.Element} this
8903          */
8904         setBox : function(box, adjust, animate){
8905             var w = box.width, h = box.height;
8906             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8907                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8908                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8909             }
8910             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8911             return this;
8912         },
8913
8914         /**
8915          * Forces the browser to repaint this element
8916          * @return {Roo.Element} this
8917          */
8918          repaint : function(){
8919             var dom = this.dom;
8920             this.addClass("x-repaint");
8921             setTimeout(function(){
8922                 Roo.get(dom).removeClass("x-repaint");
8923             }, 1);
8924             return this;
8925         },
8926
8927         /**
8928          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8929          * then it returns the calculated width of the sides (see getPadding)
8930          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8931          * @return {Object/Number}
8932          */
8933         getMargins : function(side){
8934             if(!side){
8935                 return {
8936                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8937                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8938                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8939                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8940                 };
8941             }else{
8942                 return this.addStyles(side, El.margins);
8943              }
8944         },
8945
8946         // private
8947         addStyles : function(sides, styles){
8948             var val = 0, v, w;
8949             for(var i = 0, len = sides.length; i < len; i++){
8950                 v = this.getStyle(styles[sides.charAt(i)]);
8951                 if(v){
8952                      w = parseInt(v, 10);
8953                      if(w){ val += w; }
8954                 }
8955             }
8956             return val;
8957         },
8958
8959         /**
8960          * Creates a proxy element of this element
8961          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8962          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8963          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8964          * @return {Roo.Element} The new proxy element
8965          */
8966         createProxy : function(config, renderTo, matchBox){
8967             if(renderTo){
8968                 renderTo = Roo.getDom(renderTo);
8969             }else{
8970                 renderTo = document.body;
8971             }
8972             config = typeof config == "object" ?
8973                 config : {tag : "div", cls: config};
8974             var proxy = Roo.DomHelper.append(renderTo, config, true);
8975             if(matchBox){
8976                proxy.setBox(this.getBox());
8977             }
8978             return proxy;
8979         },
8980
8981         /**
8982          * Puts a mask over this element to disable user interaction. Requires core.css.
8983          * This method can only be applied to elements which accept child nodes.
8984          * @param {String} msg (optional) A message to display in the mask
8985          * @param {String} msgCls (optional) A css class to apply to the msg element
8986          * @return {Element} The mask  element
8987          */
8988         mask : function(msg, msgCls)
8989         {
8990             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8991                 this.setStyle("position", "relative");
8992             }
8993             if(!this._mask){
8994                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8995             }
8996             this.addClass("x-masked");
8997             this._mask.setDisplayed(true);
8998             
8999             // we wander
9000             var z = 0;
9001             var dom = this.dom
9002             while (dom && dom.style) {
9003                 if (!isNaN(parseInt(dom.style.zIndex))) {
9004                     z = Math.max(z, parseInt(dom.style.zIndex));
9005                 }
9006                 dom = dom.parentNode;
9007             }
9008             // if we are masking the body - then it hides everything..
9009             if (this.dom == document.body) {
9010                 z = 1000000;
9011                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9012                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9013             }
9014            
9015             if(typeof msg == 'string'){
9016                 if(!this._maskMsg){
9017                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9018                 }
9019                 var mm = this._maskMsg;
9020                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9021                 if (mm.dom.firstChild) { // weird IE issue?
9022                     mm.dom.firstChild.innerHTML = msg;
9023                 }
9024                 mm.setDisplayed(true);
9025                 mm.center(this);
9026                 mm.setStyle('z-index', z + 102);
9027             }
9028             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9029                 this._mask.setHeight(this.getHeight());
9030             }
9031             this._mask.setStyle('z-index', z + 100);
9032             
9033             return this._mask;
9034         },
9035
9036         /**
9037          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9038          * it is cached for reuse.
9039          */
9040         unmask : function(removeEl){
9041             if(this._mask){
9042                 if(removeEl === true){
9043                     this._mask.remove();
9044                     delete this._mask;
9045                     if(this._maskMsg){
9046                         this._maskMsg.remove();
9047                         delete this._maskMsg;
9048                     }
9049                 }else{
9050                     this._mask.setDisplayed(false);
9051                     if(this._maskMsg){
9052                         this._maskMsg.setDisplayed(false);
9053                     }
9054                 }
9055             }
9056             this.removeClass("x-masked");
9057         },
9058
9059         /**
9060          * Returns true if this element is masked
9061          * @return {Boolean}
9062          */
9063         isMasked : function(){
9064             return this._mask && this._mask.isVisible();
9065         },
9066
9067         /**
9068          * Creates an iframe shim for this element to keep selects and other windowed objects from
9069          * showing through.
9070          * @return {Roo.Element} The new shim element
9071          */
9072         createShim : function(){
9073             var el = document.createElement('iframe');
9074             el.frameBorder = 'no';
9075             el.className = 'roo-shim';
9076             if(Roo.isIE && Roo.isSecure){
9077                 el.src = Roo.SSL_SECURE_URL;
9078             }
9079             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9080             shim.autoBoxAdjust = false;
9081             return shim;
9082         },
9083
9084         /**
9085          * Removes this element from the DOM and deletes it from the cache
9086          */
9087         remove : function(){
9088             if(this.dom.parentNode){
9089                 this.dom.parentNode.removeChild(this.dom);
9090             }
9091             delete El.cache[this.dom.id];
9092         },
9093
9094         /**
9095          * Sets up event handlers to add and remove a css class when the mouse is over this element
9096          * @param {String} className
9097          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9098          * mouseout events for children elements
9099          * @return {Roo.Element} this
9100          */
9101         addClassOnOver : function(className, preventFlicker){
9102             this.on("mouseover", function(){
9103                 Roo.fly(this, '_internal').addClass(className);
9104             }, this.dom);
9105             var removeFn = function(e){
9106                 if(preventFlicker !== true || !e.within(this, true)){
9107                     Roo.fly(this, '_internal').removeClass(className);
9108                 }
9109             };
9110             this.on("mouseout", removeFn, this.dom);
9111             return this;
9112         },
9113
9114         /**
9115          * Sets up event handlers to add and remove a css class when this element has the focus
9116          * @param {String} className
9117          * @return {Roo.Element} this
9118          */
9119         addClassOnFocus : function(className){
9120             this.on("focus", function(){
9121                 Roo.fly(this, '_internal').addClass(className);
9122             }, this.dom);
9123             this.on("blur", function(){
9124                 Roo.fly(this, '_internal').removeClass(className);
9125             }, this.dom);
9126             return this;
9127         },
9128         /**
9129          * 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)
9130          * @param {String} className
9131          * @return {Roo.Element} this
9132          */
9133         addClassOnClick : function(className){
9134             var dom = this.dom;
9135             this.on("mousedown", function(){
9136                 Roo.fly(dom, '_internal').addClass(className);
9137                 var d = Roo.get(document);
9138                 var fn = function(){
9139                     Roo.fly(dom, '_internal').removeClass(className);
9140                     d.removeListener("mouseup", fn);
9141                 };
9142                 d.on("mouseup", fn);
9143             });
9144             return this;
9145         },
9146
9147         /**
9148          * Stops the specified event from bubbling and optionally prevents the default action
9149          * @param {String} eventName
9150          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9151          * @return {Roo.Element} this
9152          */
9153         swallowEvent : function(eventName, preventDefault){
9154             var fn = function(e){
9155                 e.stopPropagation();
9156                 if(preventDefault){
9157                     e.preventDefault();
9158                 }
9159             };
9160             if(eventName instanceof Array){
9161                 for(var i = 0, len = eventName.length; i < len; i++){
9162                      this.on(eventName[i], fn);
9163                 }
9164                 return this;
9165             }
9166             this.on(eventName, fn);
9167             return this;
9168         },
9169
9170         /**
9171          * @private
9172          */
9173       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9174
9175         /**
9176          * Sizes this element to its parent element's dimensions performing
9177          * neccessary box adjustments.
9178          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9179          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9180          * @return {Roo.Element} this
9181          */
9182         fitToParent : function(monitorResize, targetParent) {
9183           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9184           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9185           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9186             return;
9187           }
9188           var p = Roo.get(targetParent || this.dom.parentNode);
9189           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9190           if (monitorResize === true) {
9191             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9192             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9193           }
9194           return this;
9195         },
9196
9197         /**
9198          * Gets the next sibling, skipping text nodes
9199          * @return {HTMLElement} The next sibling or null
9200          */
9201         getNextSibling : function(){
9202             var n = this.dom.nextSibling;
9203             while(n && n.nodeType != 1){
9204                 n = n.nextSibling;
9205             }
9206             return n;
9207         },
9208
9209         /**
9210          * Gets the previous sibling, skipping text nodes
9211          * @return {HTMLElement} The previous sibling or null
9212          */
9213         getPrevSibling : function(){
9214             var n = this.dom.previousSibling;
9215             while(n && n.nodeType != 1){
9216                 n = n.previousSibling;
9217             }
9218             return n;
9219         },
9220
9221
9222         /**
9223          * Appends the passed element(s) to this element
9224          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9225          * @return {Roo.Element} this
9226          */
9227         appendChild: function(el){
9228             el = Roo.get(el);
9229             el.appendTo(this);
9230             return this;
9231         },
9232
9233         /**
9234          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9235          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9236          * automatically generated with the specified attributes.
9237          * @param {HTMLElement} insertBefore (optional) a child element of this element
9238          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9239          * @return {Roo.Element} The new child element
9240          */
9241         createChild: function(config, insertBefore, returnDom){
9242             config = config || {tag:'div'};
9243             if(insertBefore){
9244                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9245             }
9246             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9247         },
9248
9249         /**
9250          * Appends this element to the passed element
9251          * @param {String/HTMLElement/Element} el The new parent element
9252          * @return {Roo.Element} this
9253          */
9254         appendTo: function(el){
9255             el = Roo.getDom(el);
9256             el.appendChild(this.dom);
9257             return this;
9258         },
9259
9260         /**
9261          * Inserts this element before the passed element in the DOM
9262          * @param {String/HTMLElement/Element} el The element to insert before
9263          * @return {Roo.Element} this
9264          */
9265         insertBefore: function(el){
9266             el = Roo.getDom(el);
9267             el.parentNode.insertBefore(this.dom, el);
9268             return this;
9269         },
9270
9271         /**
9272          * Inserts this element after the passed element in the DOM
9273          * @param {String/HTMLElement/Element} el The element to insert after
9274          * @return {Roo.Element} this
9275          */
9276         insertAfter: function(el){
9277             el = Roo.getDom(el);
9278             el.parentNode.insertBefore(this.dom, el.nextSibling);
9279             return this;
9280         },
9281
9282         /**
9283          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9284          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9285          * @return {Roo.Element} The new child
9286          */
9287         insertFirst: function(el, returnDom){
9288             el = el || {};
9289             if(typeof el == 'object' && !el.nodeType){ // dh config
9290                 return this.createChild(el, this.dom.firstChild, returnDom);
9291             }else{
9292                 el = Roo.getDom(el);
9293                 this.dom.insertBefore(el, this.dom.firstChild);
9294                 return !returnDom ? Roo.get(el) : el;
9295             }
9296         },
9297
9298         /**
9299          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9300          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9301          * @param {String} where (optional) 'before' or 'after' defaults to before
9302          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9303          * @return {Roo.Element} the inserted Element
9304          */
9305         insertSibling: function(el, where, returnDom){
9306             where = where ? where.toLowerCase() : 'before';
9307             el = el || {};
9308             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9309
9310             if(typeof el == 'object' && !el.nodeType){ // dh config
9311                 if(where == 'after' && !this.dom.nextSibling){
9312                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9313                 }else{
9314                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9315                 }
9316
9317             }else{
9318                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9319                             where == 'before' ? this.dom : this.dom.nextSibling);
9320                 if(!returnDom){
9321                     rt = Roo.get(rt);
9322                 }
9323             }
9324             return rt;
9325         },
9326
9327         /**
9328          * Creates and wraps this element with another element
9329          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9330          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9331          * @return {HTMLElement/Element} The newly created wrapper element
9332          */
9333         wrap: function(config, returnDom){
9334             if(!config){
9335                 config = {tag: "div"};
9336             }
9337             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9338             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9339             return newEl;
9340         },
9341
9342         /**
9343          * Replaces the passed element with this element
9344          * @param {String/HTMLElement/Element} el The element to replace
9345          * @return {Roo.Element} this
9346          */
9347         replace: function(el){
9348             el = Roo.get(el);
9349             this.insertBefore(el);
9350             el.remove();
9351             return this;
9352         },
9353
9354         /**
9355          * Inserts an html fragment into this element
9356          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9357          * @param {String} html The HTML fragment
9358          * @param {Boolean} returnEl True to return an Roo.Element
9359          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9360          */
9361         insertHtml : function(where, html, returnEl){
9362             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9363             return returnEl ? Roo.get(el) : el;
9364         },
9365
9366         /**
9367          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9368          * @param {Object} o The object with the attributes
9369          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9370          * @return {Roo.Element} this
9371          */
9372         set : function(o, useSet){
9373             var el = this.dom;
9374             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9375             for(var attr in o){
9376                 if(attr == "style" || typeof o[attr] == "function") continue;
9377                 if(attr=="cls"){
9378                     el.className = o["cls"];
9379                 }else{
9380                     if(useSet) el.setAttribute(attr, o[attr]);
9381                     else el[attr] = o[attr];
9382                 }
9383             }
9384             if(o.style){
9385                 Roo.DomHelper.applyStyles(el, o.style);
9386             }
9387             return this;
9388         },
9389
9390         /**
9391          * Convenience method for constructing a KeyMap
9392          * @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:
9393          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9394          * @param {Function} fn The function to call
9395          * @param {Object} scope (optional) The scope of the function
9396          * @return {Roo.KeyMap} The KeyMap created
9397          */
9398         addKeyListener : function(key, fn, scope){
9399             var config;
9400             if(typeof key != "object" || key instanceof Array){
9401                 config = {
9402                     key: key,
9403                     fn: fn,
9404                     scope: scope
9405                 };
9406             }else{
9407                 config = {
9408                     key : key.key,
9409                     shift : key.shift,
9410                     ctrl : key.ctrl,
9411                     alt : key.alt,
9412                     fn: fn,
9413                     scope: scope
9414                 };
9415             }
9416             return new Roo.KeyMap(this, config);
9417         },
9418
9419         /**
9420          * Creates a KeyMap for this element
9421          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9422          * @return {Roo.KeyMap} The KeyMap created
9423          */
9424         addKeyMap : function(config){
9425             return new Roo.KeyMap(this, config);
9426         },
9427
9428         /**
9429          * Returns true if this element is scrollable.
9430          * @return {Boolean}
9431          */
9432          isScrollable : function(){
9433             var dom = this.dom;
9434             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9435         },
9436
9437         /**
9438          * 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().
9439          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9440          * @param {Number} value The new scroll value
9441          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9442          * @return {Element} this
9443          */
9444
9445         scrollTo : function(side, value, animate){
9446             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9447             if(!animate || !A){
9448                 this.dom[prop] = value;
9449             }else{
9450                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9451                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9452             }
9453             return this;
9454         },
9455
9456         /**
9457          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9458          * within this element's scrollable range.
9459          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9460          * @param {Number} distance How far to scroll the element in pixels
9461          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9462          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9463          * was scrolled as far as it could go.
9464          */
9465          scroll : function(direction, distance, animate){
9466              if(!this.isScrollable()){
9467                  return;
9468              }
9469              var el = this.dom;
9470              var l = el.scrollLeft, t = el.scrollTop;
9471              var w = el.scrollWidth, h = el.scrollHeight;
9472              var cw = el.clientWidth, ch = el.clientHeight;
9473              direction = direction.toLowerCase();
9474              var scrolled = false;
9475              var a = this.preanim(arguments, 2);
9476              switch(direction){
9477                  case "l":
9478                  case "left":
9479                      if(w - l > cw){
9480                          var v = Math.min(l + distance, w-cw);
9481                          this.scrollTo("left", v, a);
9482                          scrolled = true;
9483                      }
9484                      break;
9485                 case "r":
9486                 case "right":
9487                      if(l > 0){
9488                          var v = Math.max(l - distance, 0);
9489                          this.scrollTo("left", v, a);
9490                          scrolled = true;
9491                      }
9492                      break;
9493                 case "t":
9494                 case "top":
9495                 case "up":
9496                      if(t > 0){
9497                          var v = Math.max(t - distance, 0);
9498                          this.scrollTo("top", v, a);
9499                          scrolled = true;
9500                      }
9501                      break;
9502                 case "b":
9503                 case "bottom":
9504                 case "down":
9505                      if(h - t > ch){
9506                          var v = Math.min(t + distance, h-ch);
9507                          this.scrollTo("top", v, a);
9508                          scrolled = true;
9509                      }
9510                      break;
9511              }
9512              return scrolled;
9513         },
9514
9515         /**
9516          * Translates the passed page coordinates into left/top css values for this element
9517          * @param {Number/Array} x The page x or an array containing [x, y]
9518          * @param {Number} y The page y
9519          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9520          */
9521         translatePoints : function(x, y){
9522             if(typeof x == 'object' || x instanceof Array){
9523                 y = x[1]; x = x[0];
9524             }
9525             var p = this.getStyle('position');
9526             var o = this.getXY();
9527
9528             var l = parseInt(this.getStyle('left'), 10);
9529             var t = parseInt(this.getStyle('top'), 10);
9530
9531             if(isNaN(l)){
9532                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9533             }
9534             if(isNaN(t)){
9535                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9536             }
9537
9538             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9539         },
9540
9541         /**
9542          * Returns the current scroll position of the element.
9543          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9544          */
9545         getScroll : function(){
9546             var d = this.dom, doc = document;
9547             if(d == doc || d == doc.body){
9548                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9549                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9550                 return {left: l, top: t};
9551             }else{
9552                 return {left: d.scrollLeft, top: d.scrollTop};
9553             }
9554         },
9555
9556         /**
9557          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9558          * are convert to standard 6 digit hex color.
9559          * @param {String} attr The css attribute
9560          * @param {String} defaultValue The default value to use when a valid color isn't found
9561          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9562          * YUI color anims.
9563          */
9564         getColor : function(attr, defaultValue, prefix){
9565             var v = this.getStyle(attr);
9566             if(!v || v == "transparent" || v == "inherit") {
9567                 return defaultValue;
9568             }
9569             var color = typeof prefix == "undefined" ? "#" : prefix;
9570             if(v.substr(0, 4) == "rgb("){
9571                 var rvs = v.slice(4, v.length -1).split(",");
9572                 for(var i = 0; i < 3; i++){
9573                     var h = parseInt(rvs[i]).toString(16);
9574                     if(h < 16){
9575                         h = "0" + h;
9576                     }
9577                     color += h;
9578                 }
9579             } else {
9580                 if(v.substr(0, 1) == "#"){
9581                     if(v.length == 4) {
9582                         for(var i = 1; i < 4; i++){
9583                             var c = v.charAt(i);
9584                             color +=  c + c;
9585                         }
9586                     }else if(v.length == 7){
9587                         color += v.substr(1);
9588                     }
9589                 }
9590             }
9591             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9592         },
9593
9594         /**
9595          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9596          * gradient background, rounded corners and a 4-way shadow.
9597          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9598          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9599          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9600          * @return {Roo.Element} this
9601          */
9602         boxWrap : function(cls){
9603             cls = cls || 'x-box';
9604             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9605             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9606             return el;
9607         },
9608
9609         /**
9610          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9611          * @param {String} namespace The namespace in which to look for the attribute
9612          * @param {String} name The attribute name
9613          * @return {String} The attribute value
9614          */
9615         getAttributeNS : Roo.isIE ? function(ns, name){
9616             var d = this.dom;
9617             var type = typeof d[ns+":"+name];
9618             if(type != 'undefined' && type != 'unknown'){
9619                 return d[ns+":"+name];
9620             }
9621             return d[name];
9622         } : function(ns, name){
9623             var d = this.dom;
9624             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9625         },
9626         
9627         
9628         /**
9629          * Sets or Returns the value the dom attribute value
9630          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9631          * @param {String} value (optional) The value to set the attribute to
9632          * @return {String} The attribute value
9633          */
9634         attr : function(name){
9635             if (arguments.length > 1) {
9636                 this.dom.setAttribute(name, arguments[1]);
9637                 return arguments[1];
9638             }
9639             if (typeof(name) == 'object') {
9640                 for(var i in name) {
9641                     this.attr(i, name[i]);
9642                 }
9643                 return name;
9644             }
9645             
9646             
9647             if (!this.dom.hasAttribute(name)) {
9648                 return undefined;
9649             }
9650             return this.dom.getAttribute(name);
9651         }
9652         
9653         
9654         
9655     };
9656
9657     var ep = El.prototype;
9658
9659     /**
9660      * Appends an event handler (Shorthand for addListener)
9661      * @param {String}   eventName     The type of event to append
9662      * @param {Function} fn        The method the event invokes
9663      * @param {Object} scope       (optional) The scope (this object) of the fn
9664      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9665      * @method
9666      */
9667     ep.on = ep.addListener;
9668         // backwards compat
9669     ep.mon = ep.addListener;
9670
9671     /**
9672      * Removes an event handler from this element (shorthand for removeListener)
9673      * @param {String} eventName the type of event to remove
9674      * @param {Function} fn the method the event invokes
9675      * @return {Roo.Element} this
9676      * @method
9677      */
9678     ep.un = ep.removeListener;
9679
9680     /**
9681      * true to automatically adjust width and height settings for box-model issues (default to true)
9682      */
9683     ep.autoBoxAdjust = true;
9684
9685     // private
9686     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9687
9688     // private
9689     El.addUnits = function(v, defaultUnit){
9690         if(v === "" || v == "auto"){
9691             return v;
9692         }
9693         if(v === undefined){
9694             return '';
9695         }
9696         if(typeof v == "number" || !El.unitPattern.test(v)){
9697             return v + (defaultUnit || 'px');
9698         }
9699         return v;
9700     };
9701
9702     // special markup used throughout Roo when box wrapping elements
9703     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>';
9704     /**
9705      * Visibility mode constant - Use visibility to hide element
9706      * @static
9707      * @type Number
9708      */
9709     El.VISIBILITY = 1;
9710     /**
9711      * Visibility mode constant - Use display to hide element
9712      * @static
9713      * @type Number
9714      */
9715     El.DISPLAY = 2;
9716
9717     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9718     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9719     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9720
9721
9722
9723     /**
9724      * @private
9725      */
9726     El.cache = {};
9727
9728     var docEl;
9729
9730     /**
9731      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9732      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9733      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9734      * @return {Element} The Element object
9735      * @static
9736      */
9737     El.get = function(el){
9738         var ex, elm, id;
9739         if(!el){ return null; }
9740         if(typeof el == "string"){ // element id
9741             if(!(elm = document.getElementById(el))){
9742                 return null;
9743             }
9744             if(ex = El.cache[el]){
9745                 ex.dom = elm;
9746             }else{
9747                 ex = El.cache[el] = new El(elm);
9748             }
9749             return ex;
9750         }else if(el.tagName){ // dom element
9751             if(!(id = el.id)){
9752                 id = Roo.id(el);
9753             }
9754             if(ex = El.cache[id]){
9755                 ex.dom = el;
9756             }else{
9757                 ex = El.cache[id] = new El(el);
9758             }
9759             return ex;
9760         }else if(el instanceof El){
9761             if(el != docEl){
9762                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9763                                                               // catch case where it hasn't been appended
9764                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9765             }
9766             return el;
9767         }else if(el.isComposite){
9768             return el;
9769         }else if(el instanceof Array){
9770             return El.select(el);
9771         }else if(el == document){
9772             // create a bogus element object representing the document object
9773             if(!docEl){
9774                 var f = function(){};
9775                 f.prototype = El.prototype;
9776                 docEl = new f();
9777                 docEl.dom = document;
9778             }
9779             return docEl;
9780         }
9781         return null;
9782     };
9783
9784     // private
9785     El.uncache = function(el){
9786         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9787             if(a[i]){
9788                 delete El.cache[a[i].id || a[i]];
9789             }
9790         }
9791     };
9792
9793     // private
9794     // Garbage collection - uncache elements/purge listeners on orphaned elements
9795     // so we don't hold a reference and cause the browser to retain them
9796     El.garbageCollect = function(){
9797         if(!Roo.enableGarbageCollector){
9798             clearInterval(El.collectorThread);
9799             return;
9800         }
9801         for(var eid in El.cache){
9802             var el = El.cache[eid], d = el.dom;
9803             // -------------------------------------------------------
9804             // Determining what is garbage:
9805             // -------------------------------------------------------
9806             // !d
9807             // dom node is null, definitely garbage
9808             // -------------------------------------------------------
9809             // !d.parentNode
9810             // no parentNode == direct orphan, definitely garbage
9811             // -------------------------------------------------------
9812             // !d.offsetParent && !document.getElementById(eid)
9813             // display none elements have no offsetParent so we will
9814             // also try to look it up by it's id. However, check
9815             // offsetParent first so we don't do unneeded lookups.
9816             // This enables collection of elements that are not orphans
9817             // directly, but somewhere up the line they have an orphan
9818             // parent.
9819             // -------------------------------------------------------
9820             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9821                 delete El.cache[eid];
9822                 if(d && Roo.enableListenerCollection){
9823                     E.purgeElement(d);
9824                 }
9825             }
9826         }
9827     }
9828     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9829
9830
9831     // dom is optional
9832     El.Flyweight = function(dom){
9833         this.dom = dom;
9834     };
9835     El.Flyweight.prototype = El.prototype;
9836
9837     El._flyweights = {};
9838     /**
9839      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9840      * the dom node can be overwritten by other code.
9841      * @param {String/HTMLElement} el The dom node or id
9842      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9843      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9844      * @static
9845      * @return {Element} The shared Element object
9846      */
9847     El.fly = function(el, named){
9848         named = named || '_global';
9849         el = Roo.getDom(el);
9850         if(!el){
9851             return null;
9852         }
9853         if(!El._flyweights[named]){
9854             El._flyweights[named] = new El.Flyweight();
9855         }
9856         El._flyweights[named].dom = el;
9857         return El._flyweights[named];
9858     };
9859
9860     /**
9861      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9862      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9863      * Shorthand of {@link Roo.Element#get}
9864      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9865      * @return {Element} The Element object
9866      * @member Roo
9867      * @method get
9868      */
9869     Roo.get = El.get;
9870     /**
9871      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9872      * the dom node can be overwritten by other code.
9873      * Shorthand of {@link Roo.Element#fly}
9874      * @param {String/HTMLElement} el The dom node or id
9875      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9876      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9877      * @static
9878      * @return {Element} The shared Element object
9879      * @member Roo
9880      * @method fly
9881      */
9882     Roo.fly = El.fly;
9883
9884     // speedy lookup for elements never to box adjust
9885     var noBoxAdjust = Roo.isStrict ? {
9886         select:1
9887     } : {
9888         input:1, select:1, textarea:1
9889     };
9890     if(Roo.isIE || Roo.isGecko){
9891         noBoxAdjust['button'] = 1;
9892     }
9893
9894
9895     Roo.EventManager.on(window, 'unload', function(){
9896         delete El.cache;
9897         delete El._flyweights;
9898     });
9899 })();
9900
9901
9902
9903
9904 if(Roo.DomQuery){
9905     Roo.Element.selectorFunction = Roo.DomQuery.select;
9906 }
9907
9908 Roo.Element.select = function(selector, unique, root){
9909     var els;
9910     if(typeof selector == "string"){
9911         els = Roo.Element.selectorFunction(selector, root);
9912     }else if(selector.length !== undefined){
9913         els = selector;
9914     }else{
9915         throw "Invalid selector";
9916     }
9917     if(unique === true){
9918         return new Roo.CompositeElement(els);
9919     }else{
9920         return new Roo.CompositeElementLite(els);
9921     }
9922 };
9923 /**
9924  * Selects elements based on the passed CSS selector to enable working on them as 1.
9925  * @param {String/Array} selector The CSS selector or an array of elements
9926  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9927  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9928  * @return {CompositeElementLite/CompositeElement}
9929  * @member Roo
9930  * @method select
9931  */
9932 Roo.select = Roo.Element.select;
9933
9934
9935
9936
9937
9938
9939
9940
9941
9942
9943
9944
9945
9946
9947 /*
9948  * Based on:
9949  * Ext JS Library 1.1.1
9950  * Copyright(c) 2006-2007, Ext JS, LLC.
9951  *
9952  * Originally Released Under LGPL - original licence link has changed is not relivant.
9953  *
9954  * Fork - LGPL
9955  * <script type="text/javascript">
9956  */
9957
9958
9959
9960 //Notifies Element that fx methods are available
9961 Roo.enableFx = true;
9962
9963 /**
9964  * @class Roo.Fx
9965  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9966  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9967  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9968  * Element effects to work.</p><br/>
9969  *
9970  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9971  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9972  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9973  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9974  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9975  * expected results and should be done with care.</p><br/>
9976  *
9977  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9978  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9979 <pre>
9980 Value  Description
9981 -----  -----------------------------
9982 tl     The top left corner
9983 t      The center of the top edge
9984 tr     The top right corner
9985 l      The center of the left edge
9986 r      The center of the right edge
9987 bl     The bottom left corner
9988 b      The center of the bottom edge
9989 br     The bottom right corner
9990 </pre>
9991  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9992  * below are common options that can be passed to any Fx method.</b>
9993  * @cfg {Function} callback A function called when the effect is finished
9994  * @cfg {Object} scope The scope of the effect function
9995  * @cfg {String} easing A valid Easing value for the effect
9996  * @cfg {String} afterCls A css class to apply after the effect
9997  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9998  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9999  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10000  * effects that end with the element being visually hidden, ignored otherwise)
10001  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10002  * a function which returns such a specification that will be applied to the Element after the effect finishes
10003  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10004  * @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
10005  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10006  */
10007 Roo.Fx = {
10008         /**
10009          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10010          * origin for the slide effect.  This function automatically handles wrapping the element with
10011          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10012          * Usage:
10013          *<pre><code>
10014 // default: slide the element in from the top
10015 el.slideIn();
10016
10017 // custom: slide the element in from the right with a 2-second duration
10018 el.slideIn('r', { duration: 2 });
10019
10020 // common config options shown with default values
10021 el.slideIn('t', {
10022     easing: 'easeOut',
10023     duration: .5
10024 });
10025 </code></pre>
10026          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10027          * @param {Object} options (optional) Object literal with any of the Fx config options
10028          * @return {Roo.Element} The Element
10029          */
10030     slideIn : function(anchor, o){
10031         var el = this.getFxEl();
10032         o = o || {};
10033
10034         el.queueFx(o, function(){
10035
10036             anchor = anchor || "t";
10037
10038             // fix display to visibility
10039             this.fixDisplay();
10040
10041             // restore values after effect
10042             var r = this.getFxRestore();
10043             var b = this.getBox();
10044             // fixed size for slide
10045             this.setSize(b);
10046
10047             // wrap if needed
10048             var wrap = this.fxWrap(r.pos, o, "hidden");
10049
10050             var st = this.dom.style;
10051             st.visibility = "visible";
10052             st.position = "absolute";
10053
10054             // clear out temp styles after slide and unwrap
10055             var after = function(){
10056                 el.fxUnwrap(wrap, r.pos, o);
10057                 st.width = r.width;
10058                 st.height = r.height;
10059                 el.afterFx(o);
10060             };
10061             // time to calc the positions
10062             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10063
10064             switch(anchor.toLowerCase()){
10065                 case "t":
10066                     wrap.setSize(b.width, 0);
10067                     st.left = st.bottom = "0";
10068                     a = {height: bh};
10069                 break;
10070                 case "l":
10071                     wrap.setSize(0, b.height);
10072                     st.right = st.top = "0";
10073                     a = {width: bw};
10074                 break;
10075                 case "r":
10076                     wrap.setSize(0, b.height);
10077                     wrap.setX(b.right);
10078                     st.left = st.top = "0";
10079                     a = {width: bw, points: pt};
10080                 break;
10081                 case "b":
10082                     wrap.setSize(b.width, 0);
10083                     wrap.setY(b.bottom);
10084                     st.left = st.top = "0";
10085                     a = {height: bh, points: pt};
10086                 break;
10087                 case "tl":
10088                     wrap.setSize(0, 0);
10089                     st.right = st.bottom = "0";
10090                     a = {width: bw, height: bh};
10091                 break;
10092                 case "bl":
10093                     wrap.setSize(0, 0);
10094                     wrap.setY(b.y+b.height);
10095                     st.right = st.top = "0";
10096                     a = {width: bw, height: bh, points: pt};
10097                 break;
10098                 case "br":
10099                     wrap.setSize(0, 0);
10100                     wrap.setXY([b.right, b.bottom]);
10101                     st.left = st.top = "0";
10102                     a = {width: bw, height: bh, points: pt};
10103                 break;
10104                 case "tr":
10105                     wrap.setSize(0, 0);
10106                     wrap.setX(b.x+b.width);
10107                     st.left = st.bottom = "0";
10108                     a = {width: bw, height: bh, points: pt};
10109                 break;
10110             }
10111             this.dom.style.visibility = "visible";
10112             wrap.show();
10113
10114             arguments.callee.anim = wrap.fxanim(a,
10115                 o,
10116                 'motion',
10117                 .5,
10118                 'easeOut', after);
10119         });
10120         return this;
10121     },
10122     
10123         /**
10124          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10125          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10126          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10127          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10128          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10129          * Usage:
10130          *<pre><code>
10131 // default: slide the element out to the top
10132 el.slideOut();
10133
10134 // custom: slide the element out to the right with a 2-second duration
10135 el.slideOut('r', { duration: 2 });
10136
10137 // common config options shown with default values
10138 el.slideOut('t', {
10139     easing: 'easeOut',
10140     duration: .5,
10141     remove: false,
10142     useDisplay: false
10143 });
10144 </code></pre>
10145          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10146          * @param {Object} options (optional) Object literal with any of the Fx config options
10147          * @return {Roo.Element} The Element
10148          */
10149     slideOut : function(anchor, o){
10150         var el = this.getFxEl();
10151         o = o || {};
10152
10153         el.queueFx(o, function(){
10154
10155             anchor = anchor || "t";
10156
10157             // restore values after effect
10158             var r = this.getFxRestore();
10159             
10160             var b = this.getBox();
10161             // fixed size for slide
10162             this.setSize(b);
10163
10164             // wrap if needed
10165             var wrap = this.fxWrap(r.pos, o, "visible");
10166
10167             var st = this.dom.style;
10168             st.visibility = "visible";
10169             st.position = "absolute";
10170
10171             wrap.setSize(b);
10172
10173             var after = function(){
10174                 if(o.useDisplay){
10175                     el.setDisplayed(false);
10176                 }else{
10177                     el.hide();
10178                 }
10179
10180                 el.fxUnwrap(wrap, r.pos, o);
10181
10182                 st.width = r.width;
10183                 st.height = r.height;
10184
10185                 el.afterFx(o);
10186             };
10187
10188             var a, zero = {to: 0};
10189             switch(anchor.toLowerCase()){
10190                 case "t":
10191                     st.left = st.bottom = "0";
10192                     a = {height: zero};
10193                 break;
10194                 case "l":
10195                     st.right = st.top = "0";
10196                     a = {width: zero};
10197                 break;
10198                 case "r":
10199                     st.left = st.top = "0";
10200                     a = {width: zero, points: {to:[b.right, b.y]}};
10201                 break;
10202                 case "b":
10203                     st.left = st.top = "0";
10204                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10205                 break;
10206                 case "tl":
10207                     st.right = st.bottom = "0";
10208                     a = {width: zero, height: zero};
10209                 break;
10210                 case "bl":
10211                     st.right = st.top = "0";
10212                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10213                 break;
10214                 case "br":
10215                     st.left = st.top = "0";
10216                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10217                 break;
10218                 case "tr":
10219                     st.left = st.bottom = "0";
10220                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10221                 break;
10222             }
10223
10224             arguments.callee.anim = wrap.fxanim(a,
10225                 o,
10226                 'motion',
10227                 .5,
10228                 "easeOut", after);
10229         });
10230         return this;
10231     },
10232
10233         /**
10234          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10235          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10236          * The element must be removed from the DOM using the 'remove' config option if desired.
10237          * Usage:
10238          *<pre><code>
10239 // default
10240 el.puff();
10241
10242 // common config options shown with default values
10243 el.puff({
10244     easing: 'easeOut',
10245     duration: .5,
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     puff : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258             this.clearOpacity();
10259             this.show();
10260
10261             // restore values after effect
10262             var r = this.getFxRestore();
10263             var st = this.dom.style;
10264
10265             var after = function(){
10266                 if(o.useDisplay){
10267                     el.setDisplayed(false);
10268                 }else{
10269                     el.hide();
10270                 }
10271
10272                 el.clearOpacity();
10273
10274                 el.setPositioning(r.pos);
10275                 st.width = r.width;
10276                 st.height = r.height;
10277                 st.fontSize = '';
10278                 el.afterFx(o);
10279             };
10280
10281             var width = this.getWidth();
10282             var height = this.getHeight();
10283
10284             arguments.callee.anim = this.fxanim({
10285                     width : {to: this.adjustWidth(width * 2)},
10286                     height : {to: this.adjustHeight(height * 2)},
10287                     points : {by: [-(width * .5), -(height * .5)]},
10288                     opacity : {to: 0},
10289                     fontSize: {to:200, unit: "%"}
10290                 },
10291                 o,
10292                 'motion',
10293                 .5,
10294                 "easeOut", after);
10295         });
10296         return this;
10297     },
10298
10299         /**
10300          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10301          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10302          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10303          * Usage:
10304          *<pre><code>
10305 // default
10306 el.switchOff();
10307
10308 // all config options shown with default values
10309 el.switchOff({
10310     easing: 'easeIn',
10311     duration: .3,
10312     remove: false,
10313     useDisplay: false
10314 });
10315 </code></pre>
10316          * @param {Object} options (optional) Object literal with any of the Fx config options
10317          * @return {Roo.Element} The Element
10318          */
10319     switchOff : function(o){
10320         var el = this.getFxEl();
10321         o = o || {};
10322
10323         el.queueFx(o, function(){
10324             this.clearOpacity();
10325             this.clip();
10326
10327             // restore values after effect
10328             var r = this.getFxRestore();
10329             var st = this.dom.style;
10330
10331             var after = function(){
10332                 if(o.useDisplay){
10333                     el.setDisplayed(false);
10334                 }else{
10335                     el.hide();
10336                 }
10337
10338                 el.clearOpacity();
10339                 el.setPositioning(r.pos);
10340                 st.width = r.width;
10341                 st.height = r.height;
10342
10343                 el.afterFx(o);
10344             };
10345
10346             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10347                 this.clearOpacity();
10348                 (function(){
10349                     this.fxanim({
10350                         height:{to:1},
10351                         points:{by:[0, this.getHeight() * .5]}
10352                     }, o, 'motion', 0.3, 'easeIn', after);
10353                 }).defer(100, this);
10354             });
10355         });
10356         return this;
10357     },
10358
10359     /**
10360      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10361      * changed using the "attr" config option) and then fading back to the original color. If no original
10362      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10363      * Usage:
10364 <pre><code>
10365 // default: highlight background to yellow
10366 el.highlight();
10367
10368 // custom: highlight foreground text to blue for 2 seconds
10369 el.highlight("0000ff", { attr: 'color', duration: 2 });
10370
10371 // common config options shown with default values
10372 el.highlight("ffff9c", {
10373     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10374     endColor: (current color) or "ffffff",
10375     easing: 'easeIn',
10376     duration: 1
10377 });
10378 </code></pre>
10379      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10380      * @param {Object} options (optional) Object literal with any of the Fx config options
10381      * @return {Roo.Element} The Element
10382      */ 
10383     highlight : function(color, o){
10384         var el = this.getFxEl();
10385         o = o || {};
10386
10387         el.queueFx(o, function(){
10388             color = color || "ffff9c";
10389             attr = o.attr || "backgroundColor";
10390
10391             this.clearOpacity();
10392             this.show();
10393
10394             var origColor = this.getColor(attr);
10395             var restoreColor = this.dom.style[attr];
10396             endColor = (o.endColor || origColor) || "ffffff";
10397
10398             var after = function(){
10399                 el.dom.style[attr] = restoreColor;
10400                 el.afterFx(o);
10401             };
10402
10403             var a = {};
10404             a[attr] = {from: color, to: endColor};
10405             arguments.callee.anim = this.fxanim(a,
10406                 o,
10407                 'color',
10408                 1,
10409                 'easeIn', after);
10410         });
10411         return this;
10412     },
10413
10414    /**
10415     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10416     * Usage:
10417 <pre><code>
10418 // default: a single light blue ripple
10419 el.frame();
10420
10421 // custom: 3 red ripples lasting 3 seconds total
10422 el.frame("ff0000", 3, { duration: 3 });
10423
10424 // common config options shown with default values
10425 el.frame("C3DAF9", 1, {
10426     duration: 1 //duration of entire animation (not each individual ripple)
10427     // Note: Easing is not configurable and will be ignored if included
10428 });
10429 </code></pre>
10430     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10431     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10432     * @param {Object} options (optional) Object literal with any of the Fx config options
10433     * @return {Roo.Element} The Element
10434     */
10435     frame : function(color, count, o){
10436         var el = this.getFxEl();
10437         o = o || {};
10438
10439         el.queueFx(o, function(){
10440             color = color || "#C3DAF9";
10441             if(color.length == 6){
10442                 color = "#" + color;
10443             }
10444             count = count || 1;
10445             duration = o.duration || 1;
10446             this.show();
10447
10448             var b = this.getBox();
10449             var animFn = function(){
10450                 var proxy = this.createProxy({
10451
10452                      style:{
10453                         visbility:"hidden",
10454                         position:"absolute",
10455                         "z-index":"35000", // yee haw
10456                         border:"0px solid " + color
10457                      }
10458                   });
10459                 var scale = Roo.isBorderBox ? 2 : 1;
10460                 proxy.animate({
10461                     top:{from:b.y, to:b.y - 20},
10462                     left:{from:b.x, to:b.x - 20},
10463                     borderWidth:{from:0, to:10},
10464                     opacity:{from:1, to:0},
10465                     height:{from:b.height, to:(b.height + (20*scale))},
10466                     width:{from:b.width, to:(b.width + (20*scale))}
10467                 }, duration, function(){
10468                     proxy.remove();
10469                 });
10470                 if(--count > 0){
10471                      animFn.defer((duration/2)*1000, this);
10472                 }else{
10473                     el.afterFx(o);
10474                 }
10475             };
10476             animFn.call(this);
10477         });
10478         return this;
10479     },
10480
10481    /**
10482     * Creates a pause before any subsequent queued effects begin.  If there are
10483     * no effects queued after the pause it will have no effect.
10484     * Usage:
10485 <pre><code>
10486 el.pause(1);
10487 </code></pre>
10488     * @param {Number} seconds The length of time to pause (in seconds)
10489     * @return {Roo.Element} The Element
10490     */
10491     pause : function(seconds){
10492         var el = this.getFxEl();
10493         var o = {};
10494
10495         el.queueFx(o, function(){
10496             setTimeout(function(){
10497                 el.afterFx(o);
10498             }, seconds * 1000);
10499         });
10500         return this;
10501     },
10502
10503    /**
10504     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10505     * using the "endOpacity" config option.
10506     * Usage:
10507 <pre><code>
10508 // default: fade in from opacity 0 to 100%
10509 el.fadeIn();
10510
10511 // custom: fade in from opacity 0 to 75% over 2 seconds
10512 el.fadeIn({ endOpacity: .75, duration: 2});
10513
10514 // common config options shown with default values
10515 el.fadeIn({
10516     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10517     easing: 'easeOut',
10518     duration: .5
10519 });
10520 </code></pre>
10521     * @param {Object} options (optional) Object literal with any of the Fx config options
10522     * @return {Roo.Element} The Element
10523     */
10524     fadeIn : function(o){
10525         var el = this.getFxEl();
10526         o = o || {};
10527         el.queueFx(o, function(){
10528             this.setOpacity(0);
10529             this.fixDisplay();
10530             this.dom.style.visibility = 'visible';
10531             var to = o.endOpacity || 1;
10532             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10533                 o, null, .5, "easeOut", function(){
10534                 if(to == 1){
10535                     this.clearOpacity();
10536                 }
10537                 el.afterFx(o);
10538             });
10539         });
10540         return this;
10541     },
10542
10543    /**
10544     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10545     * using the "endOpacity" config option.
10546     * Usage:
10547 <pre><code>
10548 // default: fade out from the element's current opacity to 0
10549 el.fadeOut();
10550
10551 // custom: fade out from the element's current opacity to 25% over 2 seconds
10552 el.fadeOut({ endOpacity: .25, duration: 2});
10553
10554 // common config options shown with default values
10555 el.fadeOut({
10556     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10557     easing: 'easeOut',
10558     duration: .5
10559     remove: false,
10560     useDisplay: false
10561 });
10562 </code></pre>
10563     * @param {Object} options (optional) Object literal with any of the Fx config options
10564     * @return {Roo.Element} The Element
10565     */
10566     fadeOut : function(o){
10567         var el = this.getFxEl();
10568         o = o || {};
10569         el.queueFx(o, function(){
10570             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10571                 o, null, .5, "easeOut", function(){
10572                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10573                      this.dom.style.display = "none";
10574                 }else{
10575                      this.dom.style.visibility = "hidden";
10576                 }
10577                 this.clearOpacity();
10578                 el.afterFx(o);
10579             });
10580         });
10581         return this;
10582     },
10583
10584    /**
10585     * Animates the transition of an element's dimensions from a starting height/width
10586     * to an ending height/width.
10587     * Usage:
10588 <pre><code>
10589 // change height and width to 100x100 pixels
10590 el.scale(100, 100);
10591
10592 // common config options shown with default values.  The height and width will default to
10593 // the element's existing values if passed as null.
10594 el.scale(
10595     [element's width],
10596     [element's height], {
10597     easing: 'easeOut',
10598     duration: .35
10599 });
10600 </code></pre>
10601     * @param {Number} width  The new width (pass undefined to keep the original width)
10602     * @param {Number} height  The new height (pass undefined to keep the original height)
10603     * @param {Object} options (optional) Object literal with any of the Fx config options
10604     * @return {Roo.Element} The Element
10605     */
10606     scale : function(w, h, o){
10607         this.shift(Roo.apply({}, o, {
10608             width: w,
10609             height: h
10610         }));
10611         return this;
10612     },
10613
10614    /**
10615     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10616     * Any of these properties not specified in the config object will not be changed.  This effect 
10617     * requires that at least one new dimension, position or opacity setting must be passed in on
10618     * the config object in order for the function to have any effect.
10619     * Usage:
10620 <pre><code>
10621 // slide the element horizontally to x position 200 while changing the height and opacity
10622 el.shift({ x: 200, height: 50, opacity: .8 });
10623
10624 // common config options shown with default values.
10625 el.shift({
10626     width: [element's width],
10627     height: [element's height],
10628     x: [element's x position],
10629     y: [element's y position],
10630     opacity: [element's opacity],
10631     easing: 'easeOut',
10632     duration: .35
10633 });
10634 </code></pre>
10635     * @param {Object} options  Object literal with any of the Fx config options
10636     * @return {Roo.Element} The Element
10637     */
10638     shift : function(o){
10639         var el = this.getFxEl();
10640         o = o || {};
10641         el.queueFx(o, function(){
10642             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10643             if(w !== undefined){
10644                 a.width = {to: this.adjustWidth(w)};
10645             }
10646             if(h !== undefined){
10647                 a.height = {to: this.adjustHeight(h)};
10648             }
10649             if(x !== undefined || y !== undefined){
10650                 a.points = {to: [
10651                     x !== undefined ? x : this.getX(),
10652                     y !== undefined ? y : this.getY()
10653                 ]};
10654             }
10655             if(op !== undefined){
10656                 a.opacity = {to: op};
10657             }
10658             if(o.xy !== undefined){
10659                 a.points = {to: o.xy};
10660             }
10661             arguments.callee.anim = this.fxanim(a,
10662                 o, 'motion', .35, "easeOut", function(){
10663                 el.afterFx(o);
10664             });
10665         });
10666         return this;
10667     },
10668
10669         /**
10670          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10671          * ending point of the effect.
10672          * Usage:
10673          *<pre><code>
10674 // default: slide the element downward while fading out
10675 el.ghost();
10676
10677 // custom: slide the element out to the right with a 2-second duration
10678 el.ghost('r', { duration: 2 });
10679
10680 // common config options shown with default values
10681 el.ghost('b', {
10682     easing: 'easeOut',
10683     duration: .5
10684     remove: false,
10685     useDisplay: false
10686 });
10687 </code></pre>
10688          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10689          * @param {Object} options (optional) Object literal with any of the Fx config options
10690          * @return {Roo.Element} The Element
10691          */
10692     ghost : function(anchor, o){
10693         var el = this.getFxEl();
10694         o = o || {};
10695
10696         el.queueFx(o, function(){
10697             anchor = anchor || "b";
10698
10699             // restore values after effect
10700             var r = this.getFxRestore();
10701             var w = this.getWidth(),
10702                 h = this.getHeight();
10703
10704             var st = this.dom.style;
10705
10706             var after = function(){
10707                 if(o.useDisplay){
10708                     el.setDisplayed(false);
10709                 }else{
10710                     el.hide();
10711                 }
10712
10713                 el.clearOpacity();
10714                 el.setPositioning(r.pos);
10715                 st.width = r.width;
10716                 st.height = r.height;
10717
10718                 el.afterFx(o);
10719             };
10720
10721             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10722             switch(anchor.toLowerCase()){
10723                 case "t":
10724                     pt.by = [0, -h];
10725                 break;
10726                 case "l":
10727                     pt.by = [-w, 0];
10728                 break;
10729                 case "r":
10730                     pt.by = [w, 0];
10731                 break;
10732                 case "b":
10733                     pt.by = [0, h];
10734                 break;
10735                 case "tl":
10736                     pt.by = [-w, -h];
10737                 break;
10738                 case "bl":
10739                     pt.by = [-w, h];
10740                 break;
10741                 case "br":
10742                     pt.by = [w, h];
10743                 break;
10744                 case "tr":
10745                     pt.by = [w, -h];
10746                 break;
10747             }
10748
10749             arguments.callee.anim = this.fxanim(a,
10750                 o,
10751                 'motion',
10752                 .5,
10753                 "easeOut", after);
10754         });
10755         return this;
10756     },
10757
10758         /**
10759          * Ensures that all effects queued after syncFx is called on the element are
10760          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10761          * @return {Roo.Element} The Element
10762          */
10763     syncFx : function(){
10764         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10765             block : false,
10766             concurrent : true,
10767             stopFx : false
10768         });
10769         return this;
10770     },
10771
10772         /**
10773          * Ensures that all effects queued after sequenceFx is called on the element are
10774          * run in sequence.  This is the opposite of {@link #syncFx}.
10775          * @return {Roo.Element} The Element
10776          */
10777     sequenceFx : function(){
10778         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10779             block : false,
10780             concurrent : false,
10781             stopFx : false
10782         });
10783         return this;
10784     },
10785
10786         /* @private */
10787     nextFx : function(){
10788         var ef = this.fxQueue[0];
10789         if(ef){
10790             ef.call(this);
10791         }
10792     },
10793
10794         /**
10795          * Returns true if the element has any effects actively running or queued, else returns false.
10796          * @return {Boolean} True if element has active effects, else false
10797          */
10798     hasActiveFx : function(){
10799         return this.fxQueue && this.fxQueue[0];
10800     },
10801
10802         /**
10803          * Stops any running effects and clears the element's internal effects queue if it contains
10804          * any additional effects that haven't started yet.
10805          * @return {Roo.Element} The Element
10806          */
10807     stopFx : function(){
10808         if(this.hasActiveFx()){
10809             var cur = this.fxQueue[0];
10810             if(cur && cur.anim && cur.anim.isAnimated()){
10811                 this.fxQueue = [cur]; // clear out others
10812                 cur.anim.stop(true);
10813             }
10814         }
10815         return this;
10816     },
10817
10818         /* @private */
10819     beforeFx : function(o){
10820         if(this.hasActiveFx() && !o.concurrent){
10821            if(o.stopFx){
10822                this.stopFx();
10823                return true;
10824            }
10825            return false;
10826         }
10827         return true;
10828     },
10829
10830         /**
10831          * Returns true if the element is currently blocking so that no other effect can be queued
10832          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10833          * used to ensure that an effect initiated by a user action runs to completion prior to the
10834          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10835          * @return {Boolean} True if blocking, else false
10836          */
10837     hasFxBlock : function(){
10838         var q = this.fxQueue;
10839         return q && q[0] && q[0].block;
10840     },
10841
10842         /* @private */
10843     queueFx : function(o, fn){
10844         if(!this.fxQueue){
10845             this.fxQueue = [];
10846         }
10847         if(!this.hasFxBlock()){
10848             Roo.applyIf(o, this.fxDefaults);
10849             if(!o.concurrent){
10850                 var run = this.beforeFx(o);
10851                 fn.block = o.block;
10852                 this.fxQueue.push(fn);
10853                 if(run){
10854                     this.nextFx();
10855                 }
10856             }else{
10857                 fn.call(this);
10858             }
10859         }
10860         return this;
10861     },
10862
10863         /* @private */
10864     fxWrap : function(pos, o, vis){
10865         var wrap;
10866         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10867             var wrapXY;
10868             if(o.fixPosition){
10869                 wrapXY = this.getXY();
10870             }
10871             var div = document.createElement("div");
10872             div.style.visibility = vis;
10873             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10874             wrap.setPositioning(pos);
10875             if(wrap.getStyle("position") == "static"){
10876                 wrap.position("relative");
10877             }
10878             this.clearPositioning('auto');
10879             wrap.clip();
10880             wrap.dom.appendChild(this.dom);
10881             if(wrapXY){
10882                 wrap.setXY(wrapXY);
10883             }
10884         }
10885         return wrap;
10886     },
10887
10888         /* @private */
10889     fxUnwrap : function(wrap, pos, o){
10890         this.clearPositioning();
10891         this.setPositioning(pos);
10892         if(!o.wrap){
10893             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10894             wrap.remove();
10895         }
10896     },
10897
10898         /* @private */
10899     getFxRestore : function(){
10900         var st = this.dom.style;
10901         return {pos: this.getPositioning(), width: st.width, height : st.height};
10902     },
10903
10904         /* @private */
10905     afterFx : function(o){
10906         if(o.afterStyle){
10907             this.applyStyles(o.afterStyle);
10908         }
10909         if(o.afterCls){
10910             this.addClass(o.afterCls);
10911         }
10912         if(o.remove === true){
10913             this.remove();
10914         }
10915         Roo.callback(o.callback, o.scope, [this]);
10916         if(!o.concurrent){
10917             this.fxQueue.shift();
10918             this.nextFx();
10919         }
10920     },
10921
10922         /* @private */
10923     getFxEl : function(){ // support for composite element fx
10924         return Roo.get(this.dom);
10925     },
10926
10927         /* @private */
10928     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10929         animType = animType || 'run';
10930         opt = opt || {};
10931         var anim = Roo.lib.Anim[animType](
10932             this.dom, args,
10933             (opt.duration || defaultDur) || .35,
10934             (opt.easing || defaultEase) || 'easeOut',
10935             function(){
10936                 Roo.callback(cb, this);
10937             },
10938             this
10939         );
10940         opt.anim = anim;
10941         return anim;
10942     }
10943 };
10944
10945 // backwords compat
10946 Roo.Fx.resize = Roo.Fx.scale;
10947
10948 //When included, Roo.Fx is automatically applied to Element so that all basic
10949 //effects are available directly via the Element API
10950 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10951  * Based on:
10952  * Ext JS Library 1.1.1
10953  * Copyright(c) 2006-2007, Ext JS, LLC.
10954  *
10955  * Originally Released Under LGPL - original licence link has changed is not relivant.
10956  *
10957  * Fork - LGPL
10958  * <script type="text/javascript">
10959  */
10960
10961
10962 /**
10963  * @class Roo.CompositeElement
10964  * Standard composite class. Creates a Roo.Element for every element in the collection.
10965  * <br><br>
10966  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10967  * actions will be performed on all the elements in this collection.</b>
10968  * <br><br>
10969  * All methods return <i>this</i> and can be chained.
10970  <pre><code>
10971  var els = Roo.select("#some-el div.some-class", true);
10972  // or select directly from an existing element
10973  var el = Roo.get('some-el');
10974  el.select('div.some-class', true);
10975
10976  els.setWidth(100); // all elements become 100 width
10977  els.hide(true); // all elements fade out and hide
10978  // or
10979  els.setWidth(100).hide(true);
10980  </code></pre>
10981  */
10982 Roo.CompositeElement = function(els){
10983     this.elements = [];
10984     this.addElements(els);
10985 };
10986 Roo.CompositeElement.prototype = {
10987     isComposite: true,
10988     addElements : function(els){
10989         if(!els) return this;
10990         if(typeof els == "string"){
10991             els = Roo.Element.selectorFunction(els);
10992         }
10993         var yels = this.elements;
10994         var index = yels.length-1;
10995         for(var i = 0, len = els.length; i < len; i++) {
10996                 yels[++index] = Roo.get(els[i]);
10997         }
10998         return this;
10999     },
11000
11001     /**
11002     * Clears this composite and adds the elements returned by the passed selector.
11003     * @param {String/Array} els A string CSS selector, an array of elements or an element
11004     * @return {CompositeElement} this
11005     */
11006     fill : function(els){
11007         this.elements = [];
11008         this.add(els);
11009         return this;
11010     },
11011
11012     /**
11013     * Filters this composite to only elements that match the passed selector.
11014     * @param {String} selector A string CSS selector
11015     * @param {Boolean} inverse return inverse filter (not matches)
11016     * @return {CompositeElement} this
11017     */
11018     filter : function(selector, inverse){
11019         var els = [];
11020         inverse = inverse || false;
11021         this.each(function(el){
11022             var match = inverse ? !el.is(selector) : el.is(selector);
11023             if(match){
11024                 els[els.length] = el.dom;
11025             }
11026         });
11027         this.fill(els);
11028         return this;
11029     },
11030
11031     invoke : function(fn, args){
11032         var els = this.elements;
11033         for(var i = 0, len = els.length; i < len; i++) {
11034                 Roo.Element.prototype[fn].apply(els[i], args);
11035         }
11036         return this;
11037     },
11038     /**
11039     * Adds elements to this composite.
11040     * @param {String/Array} els A string CSS selector, an array of elements or an element
11041     * @return {CompositeElement} this
11042     */
11043     add : function(els){
11044         if(typeof els == "string"){
11045             this.addElements(Roo.Element.selectorFunction(els));
11046         }else if(els.length !== undefined){
11047             this.addElements(els);
11048         }else{
11049             this.addElements([els]);
11050         }
11051         return this;
11052     },
11053     /**
11054     * Calls the passed function passing (el, this, index) for each element in this composite.
11055     * @param {Function} fn The function to call
11056     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11057     * @return {CompositeElement} this
11058     */
11059     each : function(fn, scope){
11060         var els = this.elements;
11061         for(var i = 0, len = els.length; i < len; i++){
11062             if(fn.call(scope || els[i], els[i], this, i) === false) {
11063                 break;
11064             }
11065         }
11066         return this;
11067     },
11068
11069     /**
11070      * Returns the Element object at the specified index
11071      * @param {Number} index
11072      * @return {Roo.Element}
11073      */
11074     item : function(index){
11075         return this.elements[index] || null;
11076     },
11077
11078     /**
11079      * Returns the first Element
11080      * @return {Roo.Element}
11081      */
11082     first : function(){
11083         return this.item(0);
11084     },
11085
11086     /**
11087      * Returns the last Element
11088      * @return {Roo.Element}
11089      */
11090     last : function(){
11091         return this.item(this.elements.length-1);
11092     },
11093
11094     /**
11095      * Returns the number of elements in this composite
11096      * @return Number
11097      */
11098     getCount : function(){
11099         return this.elements.length;
11100     },
11101
11102     /**
11103      * Returns true if this composite contains the passed element
11104      * @return Boolean
11105      */
11106     contains : function(el){
11107         return this.indexOf(el) !== -1;
11108     },
11109
11110     /**
11111      * Returns true if this composite contains the passed element
11112      * @return Boolean
11113      */
11114     indexOf : function(el){
11115         return this.elements.indexOf(Roo.get(el));
11116     },
11117
11118
11119     /**
11120     * Removes the specified element(s).
11121     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11122     * or an array of any of those.
11123     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11124     * @return {CompositeElement} this
11125     */
11126     removeElement : function(el, removeDom){
11127         if(el instanceof Array){
11128             for(var i = 0, len = el.length; i < len; i++){
11129                 this.removeElement(el[i]);
11130             }
11131             return this;
11132         }
11133         var index = typeof el == 'number' ? el : this.indexOf(el);
11134         if(index !== -1){
11135             if(removeDom){
11136                 var d = this.elements[index];
11137                 if(d.dom){
11138                     d.remove();
11139                 }else{
11140                     d.parentNode.removeChild(d);
11141                 }
11142             }
11143             this.elements.splice(index, 1);
11144         }
11145         return this;
11146     },
11147
11148     /**
11149     * Replaces the specified element with the passed element.
11150     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11151     * to replace.
11152     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11153     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11154     * @return {CompositeElement} this
11155     */
11156     replaceElement : function(el, replacement, domReplace){
11157         var index = typeof el == 'number' ? el : this.indexOf(el);
11158         if(index !== -1){
11159             if(domReplace){
11160                 this.elements[index].replaceWith(replacement);
11161             }else{
11162                 this.elements.splice(index, 1, Roo.get(replacement))
11163             }
11164         }
11165         return this;
11166     },
11167
11168     /**
11169      * Removes all elements.
11170      */
11171     clear : function(){
11172         this.elements = [];
11173     }
11174 };
11175 (function(){
11176     Roo.CompositeElement.createCall = function(proto, fnName){
11177         if(!proto[fnName]){
11178             proto[fnName] = function(){
11179                 return this.invoke(fnName, arguments);
11180             };
11181         }
11182     };
11183     for(var fnName in Roo.Element.prototype){
11184         if(typeof Roo.Element.prototype[fnName] == "function"){
11185             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11186         }
11187     };
11188 })();
11189 /*
11190  * Based on:
11191  * Ext JS Library 1.1.1
11192  * Copyright(c) 2006-2007, Ext JS, LLC.
11193  *
11194  * Originally Released Under LGPL - original licence link has changed is not relivant.
11195  *
11196  * Fork - LGPL
11197  * <script type="text/javascript">
11198  */
11199
11200 /**
11201  * @class Roo.CompositeElementLite
11202  * @extends Roo.CompositeElement
11203  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11204  <pre><code>
11205  var els = Roo.select("#some-el div.some-class");
11206  // or select directly from an existing element
11207  var el = Roo.get('some-el');
11208  el.select('div.some-class');
11209
11210  els.setWidth(100); // all elements become 100 width
11211  els.hide(true); // all elements fade out and hide
11212  // or
11213  els.setWidth(100).hide(true);
11214  </code></pre><br><br>
11215  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11216  * actions will be performed on all the elements in this collection.</b>
11217  */
11218 Roo.CompositeElementLite = function(els){
11219     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11220     this.el = new Roo.Element.Flyweight();
11221 };
11222 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11223     addElements : function(els){
11224         if(els){
11225             if(els instanceof Array){
11226                 this.elements = this.elements.concat(els);
11227             }else{
11228                 var yels = this.elements;
11229                 var index = yels.length-1;
11230                 for(var i = 0, len = els.length; i < len; i++) {
11231                     yels[++index] = els[i];
11232                 }
11233             }
11234         }
11235         return this;
11236     },
11237     invoke : function(fn, args){
11238         var els = this.elements;
11239         var el = this.el;
11240         for(var i = 0, len = els.length; i < len; i++) {
11241             el.dom = els[i];
11242                 Roo.Element.prototype[fn].apply(el, args);
11243         }
11244         return this;
11245     },
11246     /**
11247      * Returns a flyweight Element of the dom element object at the specified index
11248      * @param {Number} index
11249      * @return {Roo.Element}
11250      */
11251     item : function(index){
11252         if(!this.elements[index]){
11253             return null;
11254         }
11255         this.el.dom = this.elements[index];
11256         return this.el;
11257     },
11258
11259     // fixes scope with flyweight
11260     addListener : function(eventName, handler, scope, opt){
11261         var els = this.elements;
11262         for(var i = 0, len = els.length; i < len; i++) {
11263             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11264         }
11265         return this;
11266     },
11267
11268     /**
11269     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11270     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11271     * a reference to the dom node, use el.dom.</b>
11272     * @param {Function} fn The function to call
11273     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11274     * @return {CompositeElement} this
11275     */
11276     each : function(fn, scope){
11277         var els = this.elements;
11278         var el = this.el;
11279         for(var i = 0, len = els.length; i < len; i++){
11280             el.dom = els[i];
11281                 if(fn.call(scope || el, el, this, i) === false){
11282                 break;
11283             }
11284         }
11285         return this;
11286     },
11287
11288     indexOf : function(el){
11289         return this.elements.indexOf(Roo.getDom(el));
11290     },
11291
11292     replaceElement : function(el, replacement, domReplace){
11293         var index = typeof el == 'number' ? el : this.indexOf(el);
11294         if(index !== -1){
11295             replacement = Roo.getDom(replacement);
11296             if(domReplace){
11297                 var d = this.elements[index];
11298                 d.parentNode.insertBefore(replacement, d);
11299                 d.parentNode.removeChild(d);
11300             }
11301             this.elements.splice(index, 1, replacement);
11302         }
11303         return this;
11304     }
11305 });
11306 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11307
11308 /*
11309  * Based on:
11310  * Ext JS Library 1.1.1
11311  * Copyright(c) 2006-2007, Ext JS, LLC.
11312  *
11313  * Originally Released Under LGPL - original licence link has changed is not relivant.
11314  *
11315  * Fork - LGPL
11316  * <script type="text/javascript">
11317  */
11318
11319  
11320
11321 /**
11322  * @class Roo.data.Connection
11323  * @extends Roo.util.Observable
11324  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11325  * either to a configured URL, or to a URL specified at request time.<br><br>
11326  * <p>
11327  * Requests made by this class are asynchronous, and will return immediately. No data from
11328  * the server will be available to the statement immediately following the {@link #request} call.
11329  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11330  * <p>
11331  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11332  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11333  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11334  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11335  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11336  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11337  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11338  * standard DOM methods.
11339  * @constructor
11340  * @param {Object} config a configuration object.
11341  */
11342 Roo.data.Connection = function(config){
11343     Roo.apply(this, config);
11344     this.addEvents({
11345         /**
11346          * @event beforerequest
11347          * Fires before a network request is made to retrieve a data object.
11348          * @param {Connection} conn This Connection object.
11349          * @param {Object} options The options config object passed to the {@link #request} method.
11350          */
11351         "beforerequest" : true,
11352         /**
11353          * @event requestcomplete
11354          * Fires if the request was successfully completed.
11355          * @param {Connection} conn This Connection object.
11356          * @param {Object} response The XHR object containing the response data.
11357          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11358          * @param {Object} options The options config object passed to the {@link #request} method.
11359          */
11360         "requestcomplete" : true,
11361         /**
11362          * @event requestexception
11363          * Fires if an error HTTP status was returned from the server.
11364          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11365          * @param {Connection} conn This Connection object.
11366          * @param {Object} response The XHR object containing the response data.
11367          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11368          * @param {Object} options The options config object passed to the {@link #request} method.
11369          */
11370         "requestexception" : true
11371     });
11372     Roo.data.Connection.superclass.constructor.call(this);
11373 };
11374
11375 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11376     /**
11377      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11378      */
11379     /**
11380      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11381      * extra parameters to each request made by this object. (defaults to undefined)
11382      */
11383     /**
11384      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11385      *  to each request made by this object. (defaults to undefined)
11386      */
11387     /**
11388      * @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)
11389      */
11390     /**
11391      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11392      */
11393     timeout : 30000,
11394     /**
11395      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11396      * @type Boolean
11397      */
11398     autoAbort:false,
11399
11400     /**
11401      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11402      * @type Boolean
11403      */
11404     disableCaching: true,
11405
11406     /**
11407      * Sends an HTTP request to a remote server.
11408      * @param {Object} options An object which may contain the following properties:<ul>
11409      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11410      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11411      * request, a url encoded string or a function to call to get either.</li>
11412      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11413      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11414      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11415      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11416      * <li>options {Object} The parameter to the request call.</li>
11417      * <li>success {Boolean} True if the request succeeded.</li>
11418      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11419      * </ul></li>
11420      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11421      * The callback is passed the following parameters:<ul>
11422      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11423      * <li>options {Object} The parameter to the request call.</li>
11424      * </ul></li>
11425      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11426      * The callback is passed the following parameters:<ul>
11427      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11428      * <li>options {Object} The parameter to the request call.</li>
11429      * </ul></li>
11430      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11431      * for the callback function. Defaults to the browser window.</li>
11432      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11433      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11434      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11435      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11436      * params for the post data. Any params will be appended to the URL.</li>
11437      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11438      * </ul>
11439      * @return {Number} transactionId
11440      */
11441     request : function(o){
11442         if(this.fireEvent("beforerequest", this, o) !== false){
11443             var p = o.params;
11444
11445             if(typeof p == "function"){
11446                 p = p.call(o.scope||window, o);
11447             }
11448             if(typeof p == "object"){
11449                 p = Roo.urlEncode(o.params);
11450             }
11451             if(this.extraParams){
11452                 var extras = Roo.urlEncode(this.extraParams);
11453                 p = p ? (p + '&' + extras) : extras;
11454             }
11455
11456             var url = o.url || this.url;
11457             if(typeof url == 'function'){
11458                 url = url.call(o.scope||window, o);
11459             }
11460
11461             if(o.form){
11462                 var form = Roo.getDom(o.form);
11463                 url = url || form.action;
11464
11465                 var enctype = form.getAttribute("enctype");
11466                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11467                     return this.doFormUpload(o, p, url);
11468                 }
11469                 var f = Roo.lib.Ajax.serializeForm(form);
11470                 p = p ? (p + '&' + f) : f;
11471             }
11472
11473             var hs = o.headers;
11474             if(this.defaultHeaders){
11475                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11476                 if(!o.headers){
11477                     o.headers = hs;
11478                 }
11479             }
11480
11481             var cb = {
11482                 success: this.handleResponse,
11483                 failure: this.handleFailure,
11484                 scope: this,
11485                 argument: {options: o},
11486                 timeout : o.timeout || this.timeout
11487             };
11488
11489             var method = o.method||this.method||(p ? "POST" : "GET");
11490
11491             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11492                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11493             }
11494
11495             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11496                 if(o.autoAbort){
11497                     this.abort();
11498                 }
11499             }else if(this.autoAbort !== false){
11500                 this.abort();
11501             }
11502
11503             if((method == 'GET' && p) || o.xmlData){
11504                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11505                 p = '';
11506             }
11507             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11508             return this.transId;
11509         }else{
11510             Roo.callback(o.callback, o.scope, [o, null, null]);
11511             return null;
11512         }
11513     },
11514
11515     /**
11516      * Determine whether this object has a request outstanding.
11517      * @param {Number} transactionId (Optional) defaults to the last transaction
11518      * @return {Boolean} True if there is an outstanding request.
11519      */
11520     isLoading : function(transId){
11521         if(transId){
11522             return Roo.lib.Ajax.isCallInProgress(transId);
11523         }else{
11524             return this.transId ? true : false;
11525         }
11526     },
11527
11528     /**
11529      * Aborts any outstanding request.
11530      * @param {Number} transactionId (Optional) defaults to the last transaction
11531      */
11532     abort : function(transId){
11533         if(transId || this.isLoading()){
11534             Roo.lib.Ajax.abort(transId || this.transId);
11535         }
11536     },
11537
11538     // private
11539     handleResponse : function(response){
11540         this.transId = false;
11541         var options = response.argument.options;
11542         response.argument = options ? options.argument : null;
11543         this.fireEvent("requestcomplete", this, response, options);
11544         Roo.callback(options.success, options.scope, [response, options]);
11545         Roo.callback(options.callback, options.scope, [options, true, response]);
11546     },
11547
11548     // private
11549     handleFailure : function(response, e){
11550         this.transId = false;
11551         var options = response.argument.options;
11552         response.argument = options ? options.argument : null;
11553         this.fireEvent("requestexception", this, response, options, e);
11554         Roo.callback(options.failure, options.scope, [response, options]);
11555         Roo.callback(options.callback, options.scope, [options, false, response]);
11556     },
11557
11558     // private
11559     doFormUpload : function(o, ps, url){
11560         var id = Roo.id();
11561         var frame = document.createElement('iframe');
11562         frame.id = id;
11563         frame.name = id;
11564         frame.className = 'x-hidden';
11565         if(Roo.isIE){
11566             frame.src = Roo.SSL_SECURE_URL;
11567         }
11568         document.body.appendChild(frame);
11569
11570         if(Roo.isIE){
11571            document.frames[id].name = id;
11572         }
11573
11574         var form = Roo.getDom(o.form);
11575         form.target = id;
11576         form.method = 'POST';
11577         form.enctype = form.encoding = 'multipart/form-data';
11578         if(url){
11579             form.action = url;
11580         }
11581
11582         var hiddens, hd;
11583         if(ps){ // add dynamic params
11584             hiddens = [];
11585             ps = Roo.urlDecode(ps, false);
11586             for(var k in ps){
11587                 if(ps.hasOwnProperty(k)){
11588                     hd = document.createElement('input');
11589                     hd.type = 'hidden';
11590                     hd.name = k;
11591                     hd.value = ps[k];
11592                     form.appendChild(hd);
11593                     hiddens.push(hd);
11594                 }
11595             }
11596         }
11597
11598         function cb(){
11599             var r = {  // bogus response object
11600                 responseText : '',
11601                 responseXML : null
11602             };
11603
11604             r.argument = o ? o.argument : null;
11605
11606             try { //
11607                 var doc;
11608                 if(Roo.isIE){
11609                     doc = frame.contentWindow.document;
11610                 }else {
11611                     doc = (frame.contentDocument || window.frames[id].document);
11612                 }
11613                 if(doc && doc.body){
11614                     r.responseText = doc.body.innerHTML;
11615                 }
11616                 if(doc && doc.XMLDocument){
11617                     r.responseXML = doc.XMLDocument;
11618                 }else {
11619                     r.responseXML = doc;
11620                 }
11621             }
11622             catch(e) {
11623                 // ignore
11624             }
11625
11626             Roo.EventManager.removeListener(frame, 'load', cb, this);
11627
11628             this.fireEvent("requestcomplete", this, r, o);
11629             Roo.callback(o.success, o.scope, [r, o]);
11630             Roo.callback(o.callback, o.scope, [o, true, r]);
11631
11632             setTimeout(function(){document.body.removeChild(frame);}, 100);
11633         }
11634
11635         Roo.EventManager.on(frame, 'load', cb, this);
11636         form.submit();
11637
11638         if(hiddens){ // remove dynamic params
11639             for(var i = 0, len = hiddens.length; i < len; i++){
11640                 form.removeChild(hiddens[i]);
11641             }
11642         }
11643     }
11644 });
11645 /*
11646  * Based on:
11647  * Ext JS Library 1.1.1
11648  * Copyright(c) 2006-2007, Ext JS, LLC.
11649  *
11650  * Originally Released Under LGPL - original licence link has changed is not relivant.
11651  *
11652  * Fork - LGPL
11653  * <script type="text/javascript">
11654  */
11655  
11656 /**
11657  * Global Ajax request class.
11658  * 
11659  * @class Roo.Ajax
11660  * @extends Roo.data.Connection
11661  * @static
11662  * 
11663  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11664  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11665  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11666  * @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)
11667  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11668  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11669  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11670  */
11671 Roo.Ajax = new Roo.data.Connection({
11672     // fix up the docs
11673     /**
11674      * @scope Roo.Ajax
11675      * @type {Boolear} 
11676      */
11677     autoAbort : false,
11678
11679     /**
11680      * Serialize the passed form into a url encoded string
11681      * @scope Roo.Ajax
11682      * @param {String/HTMLElement} form
11683      * @return {String}
11684      */
11685     serializeForm : function(form){
11686         return Roo.lib.Ajax.serializeForm(form);
11687     }
11688 });/*
11689  * Based on:
11690  * Ext JS Library 1.1.1
11691  * Copyright(c) 2006-2007, Ext JS, LLC.
11692  *
11693  * Originally Released Under LGPL - original licence link has changed is not relivant.
11694  *
11695  * Fork - LGPL
11696  * <script type="text/javascript">
11697  */
11698
11699  
11700 /**
11701  * @class Roo.UpdateManager
11702  * @extends Roo.util.Observable
11703  * Provides AJAX-style update for Element object.<br><br>
11704  * Usage:<br>
11705  * <pre><code>
11706  * // Get it from a Roo.Element object
11707  * var el = Roo.get("foo");
11708  * var mgr = el.getUpdateManager();
11709  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11710  * ...
11711  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11712  * <br>
11713  * // or directly (returns the same UpdateManager instance)
11714  * var mgr = new Roo.UpdateManager("myElementId");
11715  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11716  * mgr.on("update", myFcnNeedsToKnow);
11717  * <br>
11718    // short handed call directly from the element object
11719    Roo.get("foo").load({
11720         url: "bar.php",
11721         scripts:true,
11722         params: "for=bar",
11723         text: "Loading Foo..."
11724    });
11725  * </code></pre>
11726  * @constructor
11727  * Create new UpdateManager directly.
11728  * @param {String/HTMLElement/Roo.Element} el The element to update
11729  * @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).
11730  */
11731 Roo.UpdateManager = function(el, forceNew){
11732     el = Roo.get(el);
11733     if(!forceNew && el.updateManager){
11734         return el.updateManager;
11735     }
11736     /**
11737      * The Element object
11738      * @type Roo.Element
11739      */
11740     this.el = el;
11741     /**
11742      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11743      * @type String
11744      */
11745     this.defaultUrl = null;
11746
11747     this.addEvents({
11748         /**
11749          * @event beforeupdate
11750          * Fired before an update is made, return false from your handler and the update is cancelled.
11751          * @param {Roo.Element} el
11752          * @param {String/Object/Function} url
11753          * @param {String/Object} params
11754          */
11755         "beforeupdate": true,
11756         /**
11757          * @event update
11758          * Fired after successful update is made.
11759          * @param {Roo.Element} el
11760          * @param {Object} oResponseObject The response Object
11761          */
11762         "update": true,
11763         /**
11764          * @event failure
11765          * Fired on update failure.
11766          * @param {Roo.Element} el
11767          * @param {Object} oResponseObject The response Object
11768          */
11769         "failure": true
11770     });
11771     var d = Roo.UpdateManager.defaults;
11772     /**
11773      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11774      * @type String
11775      */
11776     this.sslBlankUrl = d.sslBlankUrl;
11777     /**
11778      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11779      * @type Boolean
11780      */
11781     this.disableCaching = d.disableCaching;
11782     /**
11783      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11784      * @type String
11785      */
11786     this.indicatorText = d.indicatorText;
11787     /**
11788      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11789      * @type String
11790      */
11791     this.showLoadIndicator = d.showLoadIndicator;
11792     /**
11793      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11794      * @type Number
11795      */
11796     this.timeout = d.timeout;
11797
11798     /**
11799      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11800      * @type Boolean
11801      */
11802     this.loadScripts = d.loadScripts;
11803
11804     /**
11805      * Transaction object of current executing transaction
11806      */
11807     this.transaction = null;
11808
11809     /**
11810      * @private
11811      */
11812     this.autoRefreshProcId = null;
11813     /**
11814      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11815      * @type Function
11816      */
11817     this.refreshDelegate = this.refresh.createDelegate(this);
11818     /**
11819      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11820      * @type Function
11821      */
11822     this.updateDelegate = this.update.createDelegate(this);
11823     /**
11824      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11825      * @type Function
11826      */
11827     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11828     /**
11829      * @private
11830      */
11831     this.successDelegate = this.processSuccess.createDelegate(this);
11832     /**
11833      * @private
11834      */
11835     this.failureDelegate = this.processFailure.createDelegate(this);
11836
11837     if(!this.renderer){
11838      /**
11839       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11840       */
11841     this.renderer = new Roo.UpdateManager.BasicRenderer();
11842     }
11843     
11844     Roo.UpdateManager.superclass.constructor.call(this);
11845 };
11846
11847 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11848     /**
11849      * Get the Element this UpdateManager is bound to
11850      * @return {Roo.Element} The element
11851      */
11852     getEl : function(){
11853         return this.el;
11854     },
11855     /**
11856      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11857      * @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:
11858 <pre><code>
11859 um.update({<br/>
11860     url: "your-url.php",<br/>
11861     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11862     callback: yourFunction,<br/>
11863     scope: yourObject, //(optional scope)  <br/>
11864     discardUrl: false, <br/>
11865     nocache: false,<br/>
11866     text: "Loading...",<br/>
11867     timeout: 30,<br/>
11868     scripts: false<br/>
11869 });
11870 </code></pre>
11871      * The only required property is url. The optional properties nocache, text and scripts
11872      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11873      * @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}
11874      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11875      * @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.
11876      */
11877     update : function(url, params, callback, discardUrl){
11878         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11879             var method = this.method,
11880                 cfg;
11881             if(typeof url == "object"){ // must be config object
11882                 cfg = url;
11883                 url = cfg.url;
11884                 params = params || cfg.params;
11885                 callback = callback || cfg.callback;
11886                 discardUrl = discardUrl || cfg.discardUrl;
11887                 if(callback && cfg.scope){
11888                     callback = callback.createDelegate(cfg.scope);
11889                 }
11890                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11891                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11892                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11893                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11894                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11895             }
11896             this.showLoading();
11897             if(!discardUrl){
11898                 this.defaultUrl = url;
11899             }
11900             if(typeof url == "function"){
11901                 url = url.call(this);
11902             }
11903
11904             method = method || (params ? "POST" : "GET");
11905             if(method == "GET"){
11906                 url = this.prepareUrl(url);
11907             }
11908
11909             var o = Roo.apply(cfg ||{}, {
11910                 url : url,
11911                 params: params,
11912                 success: this.successDelegate,
11913                 failure: this.failureDelegate,
11914                 callback: undefined,
11915                 timeout: (this.timeout*1000),
11916                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11917             });
11918             Roo.log("updated manager called with timeout of " + o.timeout);
11919             this.transaction = Roo.Ajax.request(o);
11920         }
11921     },
11922
11923     /**
11924      * 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.
11925      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11926      * @param {String/HTMLElement} form The form Id or form element
11927      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11928      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11929      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11930      */
11931     formUpdate : function(form, url, reset, callback){
11932         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11933             if(typeof url == "function"){
11934                 url = url.call(this);
11935             }
11936             form = Roo.getDom(form);
11937             this.transaction = Roo.Ajax.request({
11938                 form: form,
11939                 url:url,
11940                 success: this.successDelegate,
11941                 failure: this.failureDelegate,
11942                 timeout: (this.timeout*1000),
11943                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11944             });
11945             this.showLoading.defer(1, this);
11946         }
11947     },
11948
11949     /**
11950      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11951      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11952      */
11953     refresh : function(callback){
11954         if(this.defaultUrl == null){
11955             return;
11956         }
11957         this.update(this.defaultUrl, null, callback, true);
11958     },
11959
11960     /**
11961      * Set this element to auto refresh.
11962      * @param {Number} interval How often to update (in seconds).
11963      * @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)
11964      * @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}
11965      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11966      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11967      */
11968     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11969         if(refreshNow){
11970             this.update(url || this.defaultUrl, params, callback, true);
11971         }
11972         if(this.autoRefreshProcId){
11973             clearInterval(this.autoRefreshProcId);
11974         }
11975         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11976     },
11977
11978     /**
11979      * Stop auto refresh on this element.
11980      */
11981      stopAutoRefresh : function(){
11982         if(this.autoRefreshProcId){
11983             clearInterval(this.autoRefreshProcId);
11984             delete this.autoRefreshProcId;
11985         }
11986     },
11987
11988     isAutoRefreshing : function(){
11989        return this.autoRefreshProcId ? true : false;
11990     },
11991     /**
11992      * Called to update the element to "Loading" state. Override to perform custom action.
11993      */
11994     showLoading : function(){
11995         if(this.showLoadIndicator){
11996             this.el.update(this.indicatorText);
11997         }
11998     },
11999
12000     /**
12001      * Adds unique parameter to query string if disableCaching = true
12002      * @private
12003      */
12004     prepareUrl : function(url){
12005         if(this.disableCaching){
12006             var append = "_dc=" + (new Date().getTime());
12007             if(url.indexOf("?") !== -1){
12008                 url += "&" + append;
12009             }else{
12010                 url += "?" + append;
12011             }
12012         }
12013         return url;
12014     },
12015
12016     /**
12017      * @private
12018      */
12019     processSuccess : function(response){
12020         this.transaction = null;
12021         if(response.argument.form && response.argument.reset){
12022             try{ // put in try/catch since some older FF releases had problems with this
12023                 response.argument.form.reset();
12024             }catch(e){}
12025         }
12026         if(this.loadScripts){
12027             this.renderer.render(this.el, response, this,
12028                 this.updateComplete.createDelegate(this, [response]));
12029         }else{
12030             this.renderer.render(this.el, response, this);
12031             this.updateComplete(response);
12032         }
12033     },
12034
12035     updateComplete : function(response){
12036         this.fireEvent("update", this.el, response);
12037         if(typeof response.argument.callback == "function"){
12038             response.argument.callback(this.el, true, response);
12039         }
12040     },
12041
12042     /**
12043      * @private
12044      */
12045     processFailure : function(response){
12046         this.transaction = null;
12047         this.fireEvent("failure", this.el, response);
12048         if(typeof response.argument.callback == "function"){
12049             response.argument.callback(this.el, false, response);
12050         }
12051     },
12052
12053     /**
12054      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12055      * @param {Object} renderer The object implementing the render() method
12056      */
12057     setRenderer : function(renderer){
12058         this.renderer = renderer;
12059     },
12060
12061     getRenderer : function(){
12062        return this.renderer;
12063     },
12064
12065     /**
12066      * Set the defaultUrl used for updates
12067      * @param {String/Function} defaultUrl The url or a function to call to get the url
12068      */
12069     setDefaultUrl : function(defaultUrl){
12070         this.defaultUrl = defaultUrl;
12071     },
12072
12073     /**
12074      * Aborts the executing transaction
12075      */
12076     abort : function(){
12077         if(this.transaction){
12078             Roo.Ajax.abort(this.transaction);
12079         }
12080     },
12081
12082     /**
12083      * Returns true if an update is in progress
12084      * @return {Boolean}
12085      */
12086     isUpdating : function(){
12087         if(this.transaction){
12088             return Roo.Ajax.isLoading(this.transaction);
12089         }
12090         return false;
12091     }
12092 });
12093
12094 /**
12095  * @class Roo.UpdateManager.defaults
12096  * @static (not really - but it helps the doc tool)
12097  * The defaults collection enables customizing the default properties of UpdateManager
12098  */
12099    Roo.UpdateManager.defaults = {
12100        /**
12101          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12102          * @type Number
12103          */
12104          timeout : 30,
12105
12106          /**
12107          * True to process scripts by default (Defaults to false).
12108          * @type Boolean
12109          */
12110         loadScripts : false,
12111
12112         /**
12113         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12114         * @type String
12115         */
12116         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12117         /**
12118          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12119          * @type Boolean
12120          */
12121         disableCaching : false,
12122         /**
12123          * Whether to show indicatorText when loading (Defaults to true).
12124          * @type Boolean
12125          */
12126         showLoadIndicator : true,
12127         /**
12128          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12129          * @type String
12130          */
12131         indicatorText : '<div class="loading-indicator">Loading...</div>'
12132    };
12133
12134 /**
12135  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12136  *Usage:
12137  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12138  * @param {String/HTMLElement/Roo.Element} el The element to update
12139  * @param {String} url The url
12140  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12141  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12142  * @static
12143  * @deprecated
12144  * @member Roo.UpdateManager
12145  */
12146 Roo.UpdateManager.updateElement = function(el, url, params, options){
12147     var um = Roo.get(el, true).getUpdateManager();
12148     Roo.apply(um, options);
12149     um.update(url, params, options ? options.callback : null);
12150 };
12151 // alias for backwards compat
12152 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12153 /**
12154  * @class Roo.UpdateManager.BasicRenderer
12155  * Default Content renderer. Updates the elements innerHTML with the responseText.
12156  */
12157 Roo.UpdateManager.BasicRenderer = function(){};
12158
12159 Roo.UpdateManager.BasicRenderer.prototype = {
12160     /**
12161      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12162      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12163      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12164      * @param {Roo.Element} el The element being rendered
12165      * @param {Object} response The YUI Connect response object
12166      * @param {UpdateManager} updateManager The calling update manager
12167      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12168      */
12169      render : function(el, response, updateManager, callback){
12170         el.update(response.responseText, updateManager.loadScripts, callback);
12171     }
12172 };
12173 /*
12174  * Based on:
12175  * Roo JS
12176  * (c)) Alan Knowles
12177  * Licence : LGPL
12178  */
12179
12180
12181 /**
12182  * @class Roo.DomTemplate
12183  * @extends Roo.Template
12184  * An effort at a dom based template engine..
12185  *
12186  * Similar to XTemplate, except it uses dom parsing to create the template..
12187  *
12188  * Supported features:
12189  *
12190  *  Tags:
12191
12192 <pre><code>
12193       {a_variable} - output encoded.
12194       {a_variable.format:("Y-m-d")} - call a method on the variable
12195       {a_variable:raw} - unencoded output
12196       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12197       {a_variable:this.method_on_template(...)} - call a method on the template object.
12198  
12199 </code></pre>
12200  *  The tpl tag:
12201 <pre><code>
12202         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12203         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12204         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12205         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12206   
12207 </code></pre>
12208  *      
12209  */
12210 Roo.DomTemplate = function()
12211 {
12212      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12213      if (this.html) {
12214         this.compile();
12215      }
12216 };
12217
12218
12219 Roo.extend(Roo.DomTemplate, Roo.Template, {
12220     /**
12221      * id counter for sub templates.
12222      */
12223     id : 0,
12224     /**
12225      * flag to indicate if dom parser is inside a pre,
12226      * it will strip whitespace if not.
12227      */
12228     inPre : false,
12229     
12230     /**
12231      * The various sub templates
12232      */
12233     tpls : false,
12234     
12235     
12236     
12237     /**
12238      *
12239      * basic tag replacing syntax
12240      * WORD:WORD()
12241      *
12242      * // you can fake an object call by doing this
12243      *  x.t:(test,tesT) 
12244      * 
12245      */
12246     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12247     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12248     
12249     iterChild : function (node, method) {
12250         
12251         var oldPre = this.inPre;
12252         if (node.tagName == 'PRE') {
12253             this.inPre = true;
12254         }
12255         for( var i = 0; i < node.childNodes.length; i++) {
12256             method.call(this, node.childNodes[i]);
12257         }
12258         this.inPre = oldPre;
12259     },
12260     
12261     
12262     
12263     /**
12264      * compile the template
12265      *
12266      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12267      *
12268      */
12269     compile: function()
12270     {
12271         var s = this.html;
12272         
12273         // covert the html into DOM...
12274         var doc = false;
12275         var div =false;
12276         try {
12277             doc = document.implementation.createHTMLDocument("");
12278             doc.documentElement.innerHTML =   this.html  ;
12279             div = doc.documentElement;
12280         } catch (e) {
12281             // old IE... - nasty -- it causes all sorts of issues.. with
12282             // images getting pulled from server..
12283             div = document.createElement('div');
12284             div.innerHTML = this.html;
12285         }
12286         //doc.documentElement.innerHTML = htmlBody
12287          
12288         
12289         
12290         this.tpls = [];
12291         var _t = this;
12292         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12293         
12294         var tpls = this.tpls;
12295         
12296         // create a top level template from the snippet..
12297         
12298         //Roo.log(div.innerHTML);
12299         
12300         var tpl = {
12301             uid : 'master',
12302             id : this.id++,
12303             attr : false,
12304             value : false,
12305             body : div.innerHTML,
12306             
12307             forCall : false,
12308             execCall : false,
12309             dom : div,
12310             isTop : true
12311             
12312         };
12313         tpls.unshift(tpl);
12314         
12315         
12316         // compile them...
12317         this.tpls = [];
12318         Roo.each(tpls, function(tp){
12319             this.compileTpl(tp);
12320             this.tpls[tp.id] = tp;
12321         }, this);
12322         
12323         this.master = tpls[0];
12324         return this;
12325         
12326         
12327     },
12328     
12329     compileNode : function(node, istop) {
12330         // test for
12331         //Roo.log(node);
12332         
12333         
12334         // skip anything not a tag..
12335         if (node.nodeType != 1) {
12336             if (node.nodeType == 3 && !this.inPre) {
12337                 // reduce white space..
12338                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12339                 
12340             }
12341             return;
12342         }
12343         
12344         var tpl = {
12345             uid : false,
12346             id : false,
12347             attr : false,
12348             value : false,
12349             body : '',
12350             
12351             forCall : false,
12352             execCall : false,
12353             dom : false,
12354             isTop : istop
12355             
12356             
12357         };
12358         
12359         
12360         switch(true) {
12361             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12362             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12363             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12364             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12365             // no default..
12366         }
12367         
12368         
12369         if (!tpl.attr) {
12370             // just itterate children..
12371             this.iterChild(node,this.compileNode);
12372             return;
12373         }
12374         tpl.uid = this.id++;
12375         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12376         node.removeAttribute('roo-'+ tpl.attr);
12377         if (tpl.attr != 'name') {
12378             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12379             node.parentNode.replaceChild(placeholder,  node);
12380         } else {
12381             
12382             var placeholder =  document.createElement('span');
12383             placeholder.className = 'roo-tpl-' + tpl.value;
12384             node.parentNode.replaceChild(placeholder,  node);
12385         }
12386         
12387         // parent now sees '{domtplXXXX}
12388         this.iterChild(node,this.compileNode);
12389         
12390         // we should now have node body...
12391         var div = document.createElement('div');
12392         div.appendChild(node);
12393         tpl.dom = node;
12394         // this has the unfortunate side effect of converting tagged attributes
12395         // eg. href="{...}" into %7C...%7D
12396         // this has been fixed by searching for those combo's although it's a bit hacky..
12397         
12398         
12399         tpl.body = div.innerHTML;
12400         
12401         
12402          
12403         tpl.id = tpl.uid;
12404         switch(tpl.attr) {
12405             case 'for' :
12406                 switch (tpl.value) {
12407                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12408                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12409                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12410                 }
12411                 break;
12412             
12413             case 'exec':
12414                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12415                 break;
12416             
12417             case 'if':     
12418                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12419                 break;
12420             
12421             case 'name':
12422                 tpl.id  = tpl.value; // replace non characters???
12423                 break;
12424             
12425         }
12426         
12427         
12428         this.tpls.push(tpl);
12429         
12430         
12431         
12432     },
12433     
12434     
12435     
12436     
12437     /**
12438      * Compile a segment of the template into a 'sub-template'
12439      *
12440      * 
12441      * 
12442      *
12443      */
12444     compileTpl : function(tpl)
12445     {
12446         var fm = Roo.util.Format;
12447         var useF = this.disableFormats !== true;
12448         
12449         var sep = Roo.isGecko ? "+\n" : ",\n";
12450         
12451         var undef = function(str) {
12452             Roo.debug && Roo.log("Property not found :"  + str);
12453             return '';
12454         };
12455           
12456         //Roo.log(tpl.body);
12457         
12458         
12459         
12460         var fn = function(m, lbrace, name, format, args)
12461         {
12462             //Roo.log("ARGS");
12463             //Roo.log(arguments);
12464             args = args ? args.replace(/\\'/g,"'") : args;
12465             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12466             if (typeof(format) == 'undefined') {
12467                 format =  'htmlEncode'; 
12468             }
12469             if (format == 'raw' ) {
12470                 format = false;
12471             }
12472             
12473             if(name.substr(0, 6) == 'domtpl'){
12474                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12475             }
12476             
12477             // build an array of options to determine if value is undefined..
12478             
12479             // basically get 'xxxx.yyyy' then do
12480             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12481             //    (function () { Roo.log("Property not found"); return ''; })() :
12482             //    ......
12483             
12484             var udef_ar = [];
12485             var lookfor = '';
12486             Roo.each(name.split('.'), function(st) {
12487                 lookfor += (lookfor.length ? '.': '') + st;
12488                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12489             });
12490             
12491             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12492             
12493             
12494             if(format && useF){
12495                 
12496                 args = args ? ',' + args : "";
12497                  
12498                 if(format.substr(0, 5) != "this."){
12499                     format = "fm." + format + '(';
12500                 }else{
12501                     format = 'this.call("'+ format.substr(5) + '", ';
12502                     args = ", values";
12503                 }
12504                 
12505                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12506             }
12507              
12508             if (args && args.length) {
12509                 // called with xxyx.yuu:(test,test)
12510                 // change to ()
12511                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12512             }
12513             // raw.. - :raw modifier..
12514             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12515             
12516         };
12517         var body;
12518         // branched to use + in gecko and [].join() in others
12519         if(Roo.isGecko){
12520             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12521                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12522                     "';};};";
12523         }else{
12524             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12525             body.push(tpl.body.replace(/(\r\n|\n)/g,
12526                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12527             body.push("'].join('');};};");
12528             body = body.join('');
12529         }
12530         
12531         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12532        
12533         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12534         eval(body);
12535         
12536         return this;
12537     },
12538      
12539     /**
12540      * same as applyTemplate, except it's done to one of the subTemplates
12541      * when using named templates, you can do:
12542      *
12543      * var str = pl.applySubTemplate('your-name', values);
12544      *
12545      * 
12546      * @param {Number} id of the template
12547      * @param {Object} values to apply to template
12548      * @param {Object} parent (normaly the instance of this object)
12549      */
12550     applySubTemplate : function(id, values, parent)
12551     {
12552         
12553         
12554         var t = this.tpls[id];
12555         
12556         
12557         try { 
12558             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12559                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12560                 return '';
12561             }
12562         } catch(e) {
12563             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12564             Roo.log(values);
12565           
12566             return '';
12567         }
12568         try { 
12569             
12570             if(t.execCall && t.execCall.call(this, values, parent)){
12571                 return '';
12572             }
12573         } catch(e) {
12574             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12575             Roo.log(values);
12576             return '';
12577         }
12578         
12579         try {
12580             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12581             parent = t.target ? values : parent;
12582             if(t.forCall && vs instanceof Array){
12583                 var buf = [];
12584                 for(var i = 0, len = vs.length; i < len; i++){
12585                     try {
12586                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12587                     } catch (e) {
12588                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12589                         Roo.log(e.body);
12590                         //Roo.log(t.compiled);
12591                         Roo.log(vs[i]);
12592                     }   
12593                 }
12594                 return buf.join('');
12595             }
12596         } catch (e) {
12597             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12598             Roo.log(values);
12599             return '';
12600         }
12601         try {
12602             return t.compiled.call(this, vs, parent);
12603         } catch (e) {
12604             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12605             Roo.log(e.body);
12606             //Roo.log(t.compiled);
12607             Roo.log(values);
12608             return '';
12609         }
12610     },
12611
12612    
12613
12614     applyTemplate : function(values){
12615         return this.master.compiled.call(this, values, {});
12616         //var s = this.subs;
12617     },
12618
12619     apply : function(){
12620         return this.applyTemplate.apply(this, arguments);
12621     }
12622
12623  });
12624
12625 Roo.DomTemplate.from = function(el){
12626     el = Roo.getDom(el);
12627     return new Roo.Domtemplate(el.value || el.innerHTML);
12628 };/*
12629  * Based on:
12630  * Ext JS Library 1.1.1
12631  * Copyright(c) 2006-2007, Ext JS, LLC.
12632  *
12633  * Originally Released Under LGPL - original licence link has changed is not relivant.
12634  *
12635  * Fork - LGPL
12636  * <script type="text/javascript">
12637  */
12638
12639 /**
12640  * @class Roo.util.DelayedTask
12641  * Provides a convenient method of performing setTimeout where a new
12642  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12643  * You can use this class to buffer
12644  * the keypress events for a certain number of milliseconds, and perform only if they stop
12645  * for that amount of time.
12646  * @constructor The parameters to this constructor serve as defaults and are not required.
12647  * @param {Function} fn (optional) The default function to timeout
12648  * @param {Object} scope (optional) The default scope of that timeout
12649  * @param {Array} args (optional) The default Array of arguments
12650  */
12651 Roo.util.DelayedTask = function(fn, scope, args){
12652     var id = null, d, t;
12653
12654     var call = function(){
12655         var now = new Date().getTime();
12656         if(now - t >= d){
12657             clearInterval(id);
12658             id = null;
12659             fn.apply(scope, args || []);
12660         }
12661     };
12662     /**
12663      * Cancels any pending timeout and queues a new one
12664      * @param {Number} delay The milliseconds to delay
12665      * @param {Function} newFn (optional) Overrides function passed to constructor
12666      * @param {Object} newScope (optional) Overrides scope passed to constructor
12667      * @param {Array} newArgs (optional) Overrides args passed to constructor
12668      */
12669     this.delay = function(delay, newFn, newScope, newArgs){
12670         if(id && delay != d){
12671             this.cancel();
12672         }
12673         d = delay;
12674         t = new Date().getTime();
12675         fn = newFn || fn;
12676         scope = newScope || scope;
12677         args = newArgs || args;
12678         if(!id){
12679             id = setInterval(call, d);
12680         }
12681     };
12682
12683     /**
12684      * Cancel the last queued timeout
12685      */
12686     this.cancel = function(){
12687         if(id){
12688             clearInterval(id);
12689             id = null;
12690         }
12691     };
12692 };/*
12693  * Based on:
12694  * Ext JS Library 1.1.1
12695  * Copyright(c) 2006-2007, Ext JS, LLC.
12696  *
12697  * Originally Released Under LGPL - original licence link has changed is not relivant.
12698  *
12699  * Fork - LGPL
12700  * <script type="text/javascript">
12701  */
12702  
12703  
12704 Roo.util.TaskRunner = function(interval){
12705     interval = interval || 10;
12706     var tasks = [], removeQueue = [];
12707     var id = 0;
12708     var running = false;
12709
12710     var stopThread = function(){
12711         running = false;
12712         clearInterval(id);
12713         id = 0;
12714     };
12715
12716     var startThread = function(){
12717         if(!running){
12718             running = true;
12719             id = setInterval(runTasks, interval);
12720         }
12721     };
12722
12723     var removeTask = function(task){
12724         removeQueue.push(task);
12725         if(task.onStop){
12726             task.onStop();
12727         }
12728     };
12729
12730     var runTasks = function(){
12731         if(removeQueue.length > 0){
12732             for(var i = 0, len = removeQueue.length; i < len; i++){
12733                 tasks.remove(removeQueue[i]);
12734             }
12735             removeQueue = [];
12736             if(tasks.length < 1){
12737                 stopThread();
12738                 return;
12739             }
12740         }
12741         var now = new Date().getTime();
12742         for(var i = 0, len = tasks.length; i < len; ++i){
12743             var t = tasks[i];
12744             var itime = now - t.taskRunTime;
12745             if(t.interval <= itime){
12746                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12747                 t.taskRunTime = now;
12748                 if(rt === false || t.taskRunCount === t.repeat){
12749                     removeTask(t);
12750                     return;
12751                 }
12752             }
12753             if(t.duration && t.duration <= (now - t.taskStartTime)){
12754                 removeTask(t);
12755             }
12756         }
12757     };
12758
12759     /**
12760      * Queues a new task.
12761      * @param {Object} task
12762      */
12763     this.start = function(task){
12764         tasks.push(task);
12765         task.taskStartTime = new Date().getTime();
12766         task.taskRunTime = 0;
12767         task.taskRunCount = 0;
12768         startThread();
12769         return task;
12770     };
12771
12772     this.stop = function(task){
12773         removeTask(task);
12774         return task;
12775     };
12776
12777     this.stopAll = function(){
12778         stopThread();
12779         for(var i = 0, len = tasks.length; i < len; i++){
12780             if(tasks[i].onStop){
12781                 tasks[i].onStop();
12782             }
12783         }
12784         tasks = [];
12785         removeQueue = [];
12786     };
12787 };
12788
12789 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12790  * Based on:
12791  * Ext JS Library 1.1.1
12792  * Copyright(c) 2006-2007, Ext JS, LLC.
12793  *
12794  * Originally Released Under LGPL - original licence link has changed is not relivant.
12795  *
12796  * Fork - LGPL
12797  * <script type="text/javascript">
12798  */
12799
12800  
12801 /**
12802  * @class Roo.util.MixedCollection
12803  * @extends Roo.util.Observable
12804  * A Collection class that maintains both numeric indexes and keys and exposes events.
12805  * @constructor
12806  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12807  * collection (defaults to false)
12808  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12809  * and return the key value for that item.  This is used when available to look up the key on items that
12810  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12811  * equivalent to providing an implementation for the {@link #getKey} method.
12812  */
12813 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12814     this.items = [];
12815     this.map = {};
12816     this.keys = [];
12817     this.length = 0;
12818     this.addEvents({
12819         /**
12820          * @event clear
12821          * Fires when the collection is cleared.
12822          */
12823         "clear" : true,
12824         /**
12825          * @event add
12826          * Fires when an item is added to the collection.
12827          * @param {Number} index The index at which the item was added.
12828          * @param {Object} o The item added.
12829          * @param {String} key The key associated with the added item.
12830          */
12831         "add" : true,
12832         /**
12833          * @event replace
12834          * Fires when an item is replaced in the collection.
12835          * @param {String} key he key associated with the new added.
12836          * @param {Object} old The item being replaced.
12837          * @param {Object} new The new item.
12838          */
12839         "replace" : true,
12840         /**
12841          * @event remove
12842          * Fires when an item is removed from the collection.
12843          * @param {Object} o The item being removed.
12844          * @param {String} key (optional) The key associated with the removed item.
12845          */
12846         "remove" : true,
12847         "sort" : true
12848     });
12849     this.allowFunctions = allowFunctions === true;
12850     if(keyFn){
12851         this.getKey = keyFn;
12852     }
12853     Roo.util.MixedCollection.superclass.constructor.call(this);
12854 };
12855
12856 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12857     allowFunctions : false,
12858     
12859 /**
12860  * Adds an item to the collection.
12861  * @param {String} key The key to associate with the item
12862  * @param {Object} o The item to add.
12863  * @return {Object} The item added.
12864  */
12865     add : function(key, o){
12866         if(arguments.length == 1){
12867             o = arguments[0];
12868             key = this.getKey(o);
12869         }
12870         Roo.log(this);
12871         Roo.log(o);
12872         Roo.log(key);
12873         if(typeof key == "undefined" || key === null){
12874             this.length++;
12875             this.items.push(o);
12876             this.keys.push(null);
12877         }else{
12878             var old = this.map[key];
12879             if(old){
12880                 return this.replace(key, o);
12881             }
12882             this.length++;
12883             this.items.push(o);
12884             this.map[key] = o;
12885             this.keys.push(key);
12886         }
12887         this.fireEvent("add", this.length-1, o, key);
12888         return o;
12889     },
12890        
12891 /**
12892   * MixedCollection has a generic way to fetch keys if you implement getKey.
12893 <pre><code>
12894 // normal way
12895 var mc = new Roo.util.MixedCollection();
12896 mc.add(someEl.dom.id, someEl);
12897 mc.add(otherEl.dom.id, otherEl);
12898 //and so on
12899
12900 // using getKey
12901 var mc = new Roo.util.MixedCollection();
12902 mc.getKey = function(el){
12903    return el.dom.id;
12904 };
12905 mc.add(someEl);
12906 mc.add(otherEl);
12907
12908 // or via the constructor
12909 var mc = new Roo.util.MixedCollection(false, function(el){
12910    return el.dom.id;
12911 });
12912 mc.add(someEl);
12913 mc.add(otherEl);
12914 </code></pre>
12915  * @param o {Object} The item for which to find the key.
12916  * @return {Object} The key for the passed item.
12917  */
12918     getKey : function(o){
12919          return o.id; 
12920     },
12921    
12922 /**
12923  * Replaces an item in the collection.
12924  * @param {String} key The key associated with the item to replace, or the item to replace.
12925  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12926  * @return {Object}  The new item.
12927  */
12928     replace : function(key, o){
12929         if(arguments.length == 1){
12930             o = arguments[0];
12931             key = this.getKey(o);
12932         }
12933         var old = this.item(key);
12934         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12935              return this.add(key, o);
12936         }
12937         var index = this.indexOfKey(key);
12938         this.items[index] = o;
12939         this.map[key] = o;
12940         this.fireEvent("replace", key, old, o);
12941         return o;
12942     },
12943    
12944 /**
12945  * Adds all elements of an Array or an Object to the collection.
12946  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12947  * an Array of values, each of which are added to the collection.
12948  */
12949     addAll : function(objs){
12950         if(arguments.length > 1 || objs instanceof Array){
12951             var args = arguments.length > 1 ? arguments : objs;
12952             for(var i = 0, len = args.length; i < len; i++){
12953                 this.add(args[i]);
12954             }
12955         }else{
12956             for(var key in objs){
12957                 if(this.allowFunctions || typeof objs[key] != "function"){
12958                     this.add(key, objs[key]);
12959                 }
12960             }
12961         }
12962     },
12963    
12964 /**
12965  * Executes the specified function once for every item in the collection, passing each
12966  * item as the first and only parameter. returning false from the function will stop the iteration.
12967  * @param {Function} fn The function to execute for each item.
12968  * @param {Object} scope (optional) The scope in which to execute the function.
12969  */
12970     each : function(fn, scope){
12971         var items = [].concat(this.items); // each safe for removal
12972         for(var i = 0, len = items.length; i < len; i++){
12973             if(fn.call(scope || items[i], items[i], i, len) === false){
12974                 break;
12975             }
12976         }
12977     },
12978    
12979 /**
12980  * Executes the specified function once for every key in the collection, passing each
12981  * key, and its associated item as the first two parameters.
12982  * @param {Function} fn The function to execute for each item.
12983  * @param {Object} scope (optional) The scope in which to execute the function.
12984  */
12985     eachKey : function(fn, scope){
12986         for(var i = 0, len = this.keys.length; i < len; i++){
12987             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12988         }
12989     },
12990    
12991 /**
12992  * Returns the first item in the collection which elicits a true return value from the
12993  * passed selection function.
12994  * @param {Function} fn The selection function to execute for each item.
12995  * @param {Object} scope (optional) The scope in which to execute the function.
12996  * @return {Object} The first item in the collection which returned true from the selection function.
12997  */
12998     find : function(fn, scope){
12999         for(var i = 0, len = this.items.length; i < len; i++){
13000             if(fn.call(scope || window, this.items[i], this.keys[i])){
13001                 return this.items[i];
13002             }
13003         }
13004         return null;
13005     },
13006    
13007 /**
13008  * Inserts an item at the specified index in the collection.
13009  * @param {Number} index The index to insert the item at.
13010  * @param {String} key The key to associate with the new item, or the item itself.
13011  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13012  * @return {Object} The item inserted.
13013  */
13014     insert : function(index, key, o){
13015         if(arguments.length == 2){
13016             o = arguments[1];
13017             key = this.getKey(o);
13018         }
13019         if(index >= this.length){
13020             return this.add(key, o);
13021         }
13022         this.length++;
13023         this.items.splice(index, 0, o);
13024         if(typeof key != "undefined" && key != null){
13025             this.map[key] = o;
13026         }
13027         this.keys.splice(index, 0, key);
13028         this.fireEvent("add", index, o, key);
13029         return o;
13030     },
13031    
13032 /**
13033  * Removed an item from the collection.
13034  * @param {Object} o The item to remove.
13035  * @return {Object} The item removed.
13036  */
13037     remove : function(o){
13038         return this.removeAt(this.indexOf(o));
13039     },
13040    
13041 /**
13042  * Remove an item from a specified index in the collection.
13043  * @param {Number} index The index within the collection of the item to remove.
13044  */
13045     removeAt : function(index){
13046         if(index < this.length && index >= 0){
13047             this.length--;
13048             var o = this.items[index];
13049             this.items.splice(index, 1);
13050             var key = this.keys[index];
13051             if(typeof key != "undefined"){
13052                 delete this.map[key];
13053             }
13054             this.keys.splice(index, 1);
13055             this.fireEvent("remove", o, key);
13056         }
13057     },
13058    
13059 /**
13060  * Removed an item associated with the passed key fom the collection.
13061  * @param {String} key The key of the item to remove.
13062  */
13063     removeKey : function(key){
13064         return this.removeAt(this.indexOfKey(key));
13065     },
13066    
13067 /**
13068  * Returns the number of items in the collection.
13069  * @return {Number} the number of items in the collection.
13070  */
13071     getCount : function(){
13072         return this.length; 
13073     },
13074    
13075 /**
13076  * Returns index within the collection of the passed Object.
13077  * @param {Object} o The item to find the index of.
13078  * @return {Number} index of the item.
13079  */
13080     indexOf : function(o){
13081         if(!this.items.indexOf){
13082             for(var i = 0, len = this.items.length; i < len; i++){
13083                 if(this.items[i] == o) return i;
13084             }
13085             return -1;
13086         }else{
13087             return this.items.indexOf(o);
13088         }
13089     },
13090    
13091 /**
13092  * Returns index within the collection of the passed key.
13093  * @param {String} key The key to find the index of.
13094  * @return {Number} index of the key.
13095  */
13096     indexOfKey : function(key){
13097         if(!this.keys.indexOf){
13098             for(var i = 0, len = this.keys.length; i < len; i++){
13099                 if(this.keys[i] == key) return i;
13100             }
13101             return -1;
13102         }else{
13103             return this.keys.indexOf(key);
13104         }
13105     },
13106    
13107 /**
13108  * Returns the item associated with the passed key OR index. Key has priority over index.
13109  * @param {String/Number} key The key or index of the item.
13110  * @return {Object} The item associated with the passed key.
13111  */
13112     item : function(key){
13113         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13114         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13115     },
13116     
13117 /**
13118  * Returns the item at the specified index.
13119  * @param {Number} index The index of the item.
13120  * @return {Object}
13121  */
13122     itemAt : function(index){
13123         return this.items[index];
13124     },
13125     
13126 /**
13127  * Returns the item associated with the passed key.
13128  * @param {String/Number} key The key of the item.
13129  * @return {Object} The item associated with the passed key.
13130  */
13131     key : function(key){
13132         return this.map[key];
13133     },
13134    
13135 /**
13136  * Returns true if the collection contains the passed Object as an item.
13137  * @param {Object} o  The Object to look for in the collection.
13138  * @return {Boolean} True if the collection contains the Object as an item.
13139  */
13140     contains : function(o){
13141         return this.indexOf(o) != -1;
13142     },
13143    
13144 /**
13145  * Returns true if the collection contains the passed Object as a key.
13146  * @param {String} key The key to look for in the collection.
13147  * @return {Boolean} True if the collection contains the Object as a key.
13148  */
13149     containsKey : function(key){
13150         return typeof this.map[key] != "undefined";
13151     },
13152    
13153 /**
13154  * Removes all items from the collection.
13155  */
13156     clear : function(){
13157         this.length = 0;
13158         this.items = [];
13159         this.keys = [];
13160         this.map = {};
13161         this.fireEvent("clear");
13162     },
13163    
13164 /**
13165  * Returns the first item in the collection.
13166  * @return {Object} the first item in the collection..
13167  */
13168     first : function(){
13169         return this.items[0]; 
13170     },
13171    
13172 /**
13173  * Returns the last item in the collection.
13174  * @return {Object} the last item in the collection..
13175  */
13176     last : function(){
13177         return this.items[this.length-1];   
13178     },
13179     
13180     _sort : function(property, dir, fn){
13181         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13182         fn = fn || function(a, b){
13183             return a-b;
13184         };
13185         var c = [], k = this.keys, items = this.items;
13186         for(var i = 0, len = items.length; i < len; i++){
13187             c[c.length] = {key: k[i], value: items[i], index: i};
13188         }
13189         c.sort(function(a, b){
13190             var v = fn(a[property], b[property]) * dsc;
13191             if(v == 0){
13192                 v = (a.index < b.index ? -1 : 1);
13193             }
13194             return v;
13195         });
13196         for(var i = 0, len = c.length; i < len; i++){
13197             items[i] = c[i].value;
13198             k[i] = c[i].key;
13199         }
13200         this.fireEvent("sort", this);
13201     },
13202     
13203     /**
13204      * Sorts this collection with the passed comparison function
13205      * @param {String} direction (optional) "ASC" or "DESC"
13206      * @param {Function} fn (optional) comparison function
13207      */
13208     sort : function(dir, fn){
13209         this._sort("value", dir, fn);
13210     },
13211     
13212     /**
13213      * Sorts this collection by keys
13214      * @param {String} direction (optional) "ASC" or "DESC"
13215      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13216      */
13217     keySort : function(dir, fn){
13218         this._sort("key", dir, fn || function(a, b){
13219             return String(a).toUpperCase()-String(b).toUpperCase();
13220         });
13221     },
13222     
13223     /**
13224      * Returns a range of items in this collection
13225      * @param {Number} startIndex (optional) defaults to 0
13226      * @param {Number} endIndex (optional) default to the last item
13227      * @return {Array} An array of items
13228      */
13229     getRange : function(start, end){
13230         var items = this.items;
13231         if(items.length < 1){
13232             return [];
13233         }
13234         start = start || 0;
13235         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13236         var r = [];
13237         if(start <= end){
13238             for(var i = start; i <= end; i++) {
13239                     r[r.length] = items[i];
13240             }
13241         }else{
13242             for(var i = start; i >= end; i--) {
13243                     r[r.length] = items[i];
13244             }
13245         }
13246         return r;
13247     },
13248         
13249     /**
13250      * Filter the <i>objects</i> in this collection by a specific property. 
13251      * Returns a new collection that has been filtered.
13252      * @param {String} property A property on your objects
13253      * @param {String/RegExp} value Either string that the property values 
13254      * should start with or a RegExp to test against the property
13255      * @return {MixedCollection} The new filtered collection
13256      */
13257     filter : function(property, value){
13258         if(!value.exec){ // not a regex
13259             value = String(value);
13260             if(value.length == 0){
13261                 return this.clone();
13262             }
13263             value = new RegExp("^" + Roo.escapeRe(value), "i");
13264         }
13265         return this.filterBy(function(o){
13266             return o && value.test(o[property]);
13267         });
13268         },
13269     
13270     /**
13271      * Filter by a function. * Returns a new collection that has been filtered.
13272      * The passed function will be called with each 
13273      * object in the collection. If the function returns true, the value is included 
13274      * otherwise it is filtered.
13275      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13276      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13277      * @return {MixedCollection} The new filtered collection
13278      */
13279     filterBy : function(fn, scope){
13280         var r = new Roo.util.MixedCollection();
13281         r.getKey = this.getKey;
13282         var k = this.keys, it = this.items;
13283         for(var i = 0, len = it.length; i < len; i++){
13284             if(fn.call(scope||this, it[i], k[i])){
13285                                 r.add(k[i], it[i]);
13286                         }
13287         }
13288         return r;
13289     },
13290     
13291     /**
13292      * Creates a duplicate of this collection
13293      * @return {MixedCollection}
13294      */
13295     clone : function(){
13296         var r = new Roo.util.MixedCollection();
13297         var k = this.keys, it = this.items;
13298         for(var i = 0, len = it.length; i < len; i++){
13299             r.add(k[i], it[i]);
13300         }
13301         r.getKey = this.getKey;
13302         return r;
13303     }
13304 });
13305 /**
13306  * Returns the item associated with the passed key or index.
13307  * @method
13308  * @param {String/Number} key The key or index of the item.
13309  * @return {Object} The item associated with the passed key.
13310  */
13311 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13312  * Based on:
13313  * Ext JS Library 1.1.1
13314  * Copyright(c) 2006-2007, Ext JS, LLC.
13315  *
13316  * Originally Released Under LGPL - original licence link has changed is not relivant.
13317  *
13318  * Fork - LGPL
13319  * <script type="text/javascript">
13320  */
13321 /**
13322  * @class Roo.util.JSON
13323  * Modified version of Douglas Crockford"s json.js that doesn"t
13324  * mess with the Object prototype 
13325  * http://www.json.org/js.html
13326  * @singleton
13327  */
13328 Roo.util.JSON = new (function(){
13329     var useHasOwn = {}.hasOwnProperty ? true : false;
13330     
13331     // crashes Safari in some instances
13332     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13333     
13334     var pad = function(n) {
13335         return n < 10 ? "0" + n : n;
13336     };
13337     
13338     var m = {
13339         "\b": '\\b',
13340         "\t": '\\t',
13341         "\n": '\\n',
13342         "\f": '\\f',
13343         "\r": '\\r',
13344         '"' : '\\"',
13345         "\\": '\\\\'
13346     };
13347
13348     var encodeString = function(s){
13349         if (/["\\\x00-\x1f]/.test(s)) {
13350             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13351                 var c = m[b];
13352                 if(c){
13353                     return c;
13354                 }
13355                 c = b.charCodeAt();
13356                 return "\\u00" +
13357                     Math.floor(c / 16).toString(16) +
13358                     (c % 16).toString(16);
13359             }) + '"';
13360         }
13361         return '"' + s + '"';
13362     };
13363     
13364     var encodeArray = function(o){
13365         var a = ["["], b, i, l = o.length, v;
13366             for (i = 0; i < l; i += 1) {
13367                 v = o[i];
13368                 switch (typeof v) {
13369                     case "undefined":
13370                     case "function":
13371                     case "unknown":
13372                         break;
13373                     default:
13374                         if (b) {
13375                             a.push(',');
13376                         }
13377                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13378                         b = true;
13379                 }
13380             }
13381             a.push("]");
13382             return a.join("");
13383     };
13384     
13385     var encodeDate = function(o){
13386         return '"' + o.getFullYear() + "-" +
13387                 pad(o.getMonth() + 1) + "-" +
13388                 pad(o.getDate()) + "T" +
13389                 pad(o.getHours()) + ":" +
13390                 pad(o.getMinutes()) + ":" +
13391                 pad(o.getSeconds()) + '"';
13392     };
13393     
13394     /**
13395      * Encodes an Object, Array or other value
13396      * @param {Mixed} o The variable to encode
13397      * @return {String} The JSON string
13398      */
13399     this.encode = function(o)
13400     {
13401         // should this be extended to fully wrap stringify..
13402         
13403         if(typeof o == "undefined" || o === null){
13404             return "null";
13405         }else if(o instanceof Array){
13406             return encodeArray(o);
13407         }else if(o instanceof Date){
13408             return encodeDate(o);
13409         }else if(typeof o == "string"){
13410             return encodeString(o);
13411         }else if(typeof o == "number"){
13412             return isFinite(o) ? String(o) : "null";
13413         }else if(typeof o == "boolean"){
13414             return String(o);
13415         }else {
13416             var a = ["{"], b, i, v;
13417             for (i in o) {
13418                 if(!useHasOwn || o.hasOwnProperty(i)) {
13419                     v = o[i];
13420                     switch (typeof v) {
13421                     case "undefined":
13422                     case "function":
13423                     case "unknown":
13424                         break;
13425                     default:
13426                         if(b){
13427                             a.push(',');
13428                         }
13429                         a.push(this.encode(i), ":",
13430                                 v === null ? "null" : this.encode(v));
13431                         b = true;
13432                     }
13433                 }
13434             }
13435             a.push("}");
13436             return a.join("");
13437         }
13438     };
13439     
13440     /**
13441      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13442      * @param {String} json The JSON string
13443      * @return {Object} The resulting object
13444      */
13445     this.decode = function(json){
13446         
13447         return  /** eval:var:json */ eval("(" + json + ')');
13448     };
13449 })();
13450 /** 
13451  * Shorthand for {@link Roo.util.JSON#encode}
13452  * @member Roo encode 
13453  * @method */
13454 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13455 /** 
13456  * Shorthand for {@link Roo.util.JSON#decode}
13457  * @member Roo decode 
13458  * @method */
13459 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13460 /*
13461  * Based on:
13462  * Ext JS Library 1.1.1
13463  * Copyright(c) 2006-2007, Ext JS, LLC.
13464  *
13465  * Originally Released Under LGPL - original licence link has changed is not relivant.
13466  *
13467  * Fork - LGPL
13468  * <script type="text/javascript">
13469  */
13470  
13471 /**
13472  * @class Roo.util.Format
13473  * Reusable data formatting functions
13474  * @singleton
13475  */
13476 Roo.util.Format = function(){
13477     var trimRe = /^\s+|\s+$/g;
13478     return {
13479         /**
13480          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13481          * @param {String} value The string to truncate
13482          * @param {Number} length The maximum length to allow before truncating
13483          * @return {String} The converted text
13484          */
13485         ellipsis : function(value, len){
13486             if(value && value.length > len){
13487                 return value.substr(0, len-3)+"...";
13488             }
13489             return value;
13490         },
13491
13492         /**
13493          * Checks a reference and converts it to empty string if it is undefined
13494          * @param {Mixed} value Reference to check
13495          * @return {Mixed} Empty string if converted, otherwise the original value
13496          */
13497         undef : function(value){
13498             return typeof value != "undefined" ? value : "";
13499         },
13500
13501         /**
13502          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13503          * @param {String} value The string to encode
13504          * @return {String} The encoded text
13505          */
13506         htmlEncode : function(value){
13507             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13508         },
13509
13510         /**
13511          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13512          * @param {String} value The string to decode
13513          * @return {String} The decoded text
13514          */
13515         htmlDecode : function(value){
13516             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13517         },
13518
13519         /**
13520          * Trims any whitespace from either side of a string
13521          * @param {String} value The text to trim
13522          * @return {String} The trimmed text
13523          */
13524         trim : function(value){
13525             return String(value).replace(trimRe, "");
13526         },
13527
13528         /**
13529          * Returns a substring from within an original string
13530          * @param {String} value The original text
13531          * @param {Number} start The start index of the substring
13532          * @param {Number} length The length of the substring
13533          * @return {String} The substring
13534          */
13535         substr : function(value, start, length){
13536             return String(value).substr(start, length);
13537         },
13538
13539         /**
13540          * Converts a string to all lower case letters
13541          * @param {String} value The text to convert
13542          * @return {String} The converted text
13543          */
13544         lowercase : function(value){
13545             return String(value).toLowerCase();
13546         },
13547
13548         /**
13549          * Converts a string to all upper case letters
13550          * @param {String} value The text to convert
13551          * @return {String} The converted text
13552          */
13553         uppercase : function(value){
13554             return String(value).toUpperCase();
13555         },
13556
13557         /**
13558          * Converts the first character only of a string to upper case
13559          * @param {String} value The text to convert
13560          * @return {String} The converted text
13561          */
13562         capitalize : function(value){
13563             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13564         },
13565
13566         // private
13567         call : function(value, fn){
13568             if(arguments.length > 2){
13569                 var args = Array.prototype.slice.call(arguments, 2);
13570                 args.unshift(value);
13571                  
13572                 return /** eval:var:value */  eval(fn).apply(window, args);
13573             }else{
13574                 /** eval:var:value */
13575                 return /** eval:var:value */ eval(fn).call(window, value);
13576             }
13577         },
13578
13579        
13580         /**
13581          * safer version of Math.toFixed..??/
13582          * @param {Number/String} value The numeric value to format
13583          * @param {Number/String} value Decimal places 
13584          * @return {String} The formatted currency string
13585          */
13586         toFixed : function(v, n)
13587         {
13588             // why not use to fixed - precision is buggered???
13589             if (!n) {
13590                 return Math.round(v-0);
13591             }
13592             var fact = Math.pow(10,n+1);
13593             v = (Math.round((v-0)*fact))/fact;
13594             var z = (''+fact).substring(2);
13595             if (v == Math.floor(v)) {
13596                 return Math.floor(v) + '.' + z;
13597             }
13598             
13599             // now just padd decimals..
13600             var ps = String(v).split('.');
13601             var fd = (ps[1] + z);
13602             var r = fd.substring(0,n); 
13603             var rm = fd.substring(n); 
13604             if (rm < 5) {
13605                 return ps[0] + '.' + r;
13606             }
13607             r*=1; // turn it into a number;
13608             r++;
13609             if (String(r).length != n) {
13610                 ps[0]*=1;
13611                 ps[0]++;
13612                 r = String(r).substring(1); // chop the end off.
13613             }
13614             
13615             return ps[0] + '.' + r;
13616              
13617         },
13618         
13619         /**
13620          * Format a number as US currency
13621          * @param {Number/String} value The numeric value to format
13622          * @return {String} The formatted currency string
13623          */
13624         usMoney : function(v){
13625             return '$' + Roo.util.Format.number(v);
13626         },
13627         
13628         /**
13629          * Format a number
13630          * eventually this should probably emulate php's number_format
13631          * @param {Number/String} value The numeric value to format
13632          * @param {Number} decimals number of decimal places
13633          * @return {String} The formatted currency string
13634          */
13635         number : function(v,decimals)
13636         {
13637             // multiply and round.
13638             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13639             var mul = Math.pow(10, decimals);
13640             var zero = String(mul).substring(1);
13641             v = (Math.round((v-0)*mul))/mul;
13642             
13643             // if it's '0' number.. then
13644             
13645             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13646             v = String(v);
13647             var ps = v.split('.');
13648             var whole = ps[0];
13649             
13650             
13651             var r = /(\d+)(\d{3})/;
13652             // add comma's
13653             while (r.test(whole)) {
13654                 whole = whole.replace(r, '$1' + ',' + '$2');
13655             }
13656             
13657             
13658             var sub = ps[1] ?
13659                     // has decimals..
13660                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13661                     // does not have decimals
13662                     (decimals ? ('.' + zero) : '');
13663             
13664             
13665             return whole + sub ;
13666         },
13667         
13668         /**
13669          * Parse a value into a formatted date using the specified format pattern.
13670          * @param {Mixed} value The value to format
13671          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13672          * @return {String} The formatted date string
13673          */
13674         date : function(v, format){
13675             if(!v){
13676                 return "";
13677             }
13678             if(!(v instanceof Date)){
13679                 v = new Date(Date.parse(v));
13680             }
13681             return v.dateFormat(format || Roo.util.Format.defaults.date);
13682         },
13683
13684         /**
13685          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13686          * @param {String} format Any valid date format string
13687          * @return {Function} The date formatting function
13688          */
13689         dateRenderer : function(format){
13690             return function(v){
13691                 return Roo.util.Format.date(v, format);  
13692             };
13693         },
13694
13695         // private
13696         stripTagsRE : /<\/?[^>]+>/gi,
13697         
13698         /**
13699          * Strips all HTML tags
13700          * @param {Mixed} value The text from which to strip tags
13701          * @return {String} The stripped text
13702          */
13703         stripTags : function(v){
13704             return !v ? v : String(v).replace(this.stripTagsRE, "");
13705         }
13706     };
13707 }();
13708 Roo.util.Format.defaults = {
13709     date : 'd/M/Y'
13710 };/*
13711  * Based on:
13712  * Ext JS Library 1.1.1
13713  * Copyright(c) 2006-2007, Ext JS, LLC.
13714  *
13715  * Originally Released Under LGPL - original licence link has changed is not relivant.
13716  *
13717  * Fork - LGPL
13718  * <script type="text/javascript">
13719  */
13720
13721
13722  
13723
13724 /**
13725  * @class Roo.MasterTemplate
13726  * @extends Roo.Template
13727  * Provides a template that can have child templates. The syntax is:
13728 <pre><code>
13729 var t = new Roo.MasterTemplate(
13730         '&lt;select name="{name}"&gt;',
13731                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13732         '&lt;/select&gt;'
13733 );
13734 t.add('options', {value: 'foo', text: 'bar'});
13735 // or you can add multiple child elements in one shot
13736 t.addAll('options', [
13737     {value: 'foo', text: 'bar'},
13738     {value: 'foo2', text: 'bar2'},
13739     {value: 'foo3', text: 'bar3'}
13740 ]);
13741 // then append, applying the master template values
13742 t.append('my-form', {name: 'my-select'});
13743 </code></pre>
13744 * A name attribute for the child template is not required if you have only one child
13745 * template or you want to refer to them by index.
13746  */
13747 Roo.MasterTemplate = function(){
13748     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13749     this.originalHtml = this.html;
13750     var st = {};
13751     var m, re = this.subTemplateRe;
13752     re.lastIndex = 0;
13753     var subIndex = 0;
13754     while(m = re.exec(this.html)){
13755         var name = m[1], content = m[2];
13756         st[subIndex] = {
13757             name: name,
13758             index: subIndex,
13759             buffer: [],
13760             tpl : new Roo.Template(content)
13761         };
13762         if(name){
13763             st[name] = st[subIndex];
13764         }
13765         st[subIndex].tpl.compile();
13766         st[subIndex].tpl.call = this.call.createDelegate(this);
13767         subIndex++;
13768     }
13769     this.subCount = subIndex;
13770     this.subs = st;
13771 };
13772 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13773     /**
13774     * The regular expression used to match sub templates
13775     * @type RegExp
13776     * @property
13777     */
13778     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13779
13780     /**
13781      * Applies the passed values to a child template.
13782      * @param {String/Number} name (optional) The name or index of the child template
13783      * @param {Array/Object} values The values to be applied to the template
13784      * @return {MasterTemplate} this
13785      */
13786      add : function(name, values){
13787         if(arguments.length == 1){
13788             values = arguments[0];
13789             name = 0;
13790         }
13791         var s = this.subs[name];
13792         s.buffer[s.buffer.length] = s.tpl.apply(values);
13793         return this;
13794     },
13795
13796     /**
13797      * Applies all the passed values to a child template.
13798      * @param {String/Number} name (optional) The name or index of the child template
13799      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13800      * @param {Boolean} reset (optional) True to reset the template first
13801      * @return {MasterTemplate} this
13802      */
13803     fill : function(name, values, reset){
13804         var a = arguments;
13805         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13806             values = a[0];
13807             name = 0;
13808             reset = a[1];
13809         }
13810         if(reset){
13811             this.reset();
13812         }
13813         for(var i = 0, len = values.length; i < len; i++){
13814             this.add(name, values[i]);
13815         }
13816         return this;
13817     },
13818
13819     /**
13820      * Resets the template for reuse
13821      * @return {MasterTemplate} this
13822      */
13823      reset : function(){
13824         var s = this.subs;
13825         for(var i = 0; i < this.subCount; i++){
13826             s[i].buffer = [];
13827         }
13828         return this;
13829     },
13830
13831     applyTemplate : function(values){
13832         var s = this.subs;
13833         var replaceIndex = -1;
13834         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13835             return s[++replaceIndex].buffer.join("");
13836         });
13837         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13838     },
13839
13840     apply : function(){
13841         return this.applyTemplate.apply(this, arguments);
13842     },
13843
13844     compile : function(){return this;}
13845 });
13846
13847 /**
13848  * Alias for fill().
13849  * @method
13850  */
13851 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13852  /**
13853  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13854  * var tpl = Roo.MasterTemplate.from('element-id');
13855  * @param {String/HTMLElement} el
13856  * @param {Object} config
13857  * @static
13858  */
13859 Roo.MasterTemplate.from = function(el, config){
13860     el = Roo.getDom(el);
13861     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13862 };/*
13863  * Based on:
13864  * Ext JS Library 1.1.1
13865  * Copyright(c) 2006-2007, Ext JS, LLC.
13866  *
13867  * Originally Released Under LGPL - original licence link has changed is not relivant.
13868  *
13869  * Fork - LGPL
13870  * <script type="text/javascript">
13871  */
13872
13873  
13874 /**
13875  * @class Roo.util.CSS
13876  * Utility class for manipulating CSS rules
13877  * @singleton
13878  */
13879 Roo.util.CSS = function(){
13880         var rules = null;
13881         var doc = document;
13882
13883     var camelRe = /(-[a-z])/gi;
13884     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13885
13886    return {
13887    /**
13888     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13889     * tag and appended to the HEAD of the document.
13890     * @param {String|Object} cssText The text containing the css rules
13891     * @param {String} id An id to add to the stylesheet for later removal
13892     * @return {StyleSheet}
13893     */
13894     createStyleSheet : function(cssText, id){
13895         var ss;
13896         var head = doc.getElementsByTagName("head")[0];
13897         var nrules = doc.createElement("style");
13898         nrules.setAttribute("type", "text/css");
13899         if(id){
13900             nrules.setAttribute("id", id);
13901         }
13902         if (typeof(cssText) != 'string') {
13903             // support object maps..
13904             // not sure if this a good idea.. 
13905             // perhaps it should be merged with the general css handling
13906             // and handle js style props.
13907             var cssTextNew = [];
13908             for(var n in cssText) {
13909                 var citems = [];
13910                 for(var k in cssText[n]) {
13911                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13912                 }
13913                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13914                 
13915             }
13916             cssText = cssTextNew.join("\n");
13917             
13918         }
13919        
13920        
13921        if(Roo.isIE){
13922            head.appendChild(nrules);
13923            ss = nrules.styleSheet;
13924            ss.cssText = cssText;
13925        }else{
13926            try{
13927                 nrules.appendChild(doc.createTextNode(cssText));
13928            }catch(e){
13929                nrules.cssText = cssText; 
13930            }
13931            head.appendChild(nrules);
13932            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13933        }
13934        this.cacheStyleSheet(ss);
13935        return ss;
13936    },
13937
13938    /**
13939     * Removes a style or link tag by id
13940     * @param {String} id The id of the tag
13941     */
13942    removeStyleSheet : function(id){
13943        var existing = doc.getElementById(id);
13944        if(existing){
13945            existing.parentNode.removeChild(existing);
13946        }
13947    },
13948
13949    /**
13950     * Dynamically swaps an existing stylesheet reference for a new one
13951     * @param {String} id The id of an existing link tag to remove
13952     * @param {String} url The href of the new stylesheet to include
13953     */
13954    swapStyleSheet : function(id, url){
13955        this.removeStyleSheet(id);
13956        var ss = doc.createElement("link");
13957        ss.setAttribute("rel", "stylesheet");
13958        ss.setAttribute("type", "text/css");
13959        ss.setAttribute("id", id);
13960        ss.setAttribute("href", url);
13961        doc.getElementsByTagName("head")[0].appendChild(ss);
13962    },
13963    
13964    /**
13965     * Refresh the rule cache if you have dynamically added stylesheets
13966     * @return {Object} An object (hash) of rules indexed by selector
13967     */
13968    refreshCache : function(){
13969        return this.getRules(true);
13970    },
13971
13972    // private
13973    cacheStyleSheet : function(stylesheet){
13974        if(!rules){
13975            rules = {};
13976        }
13977        try{// try catch for cross domain access issue
13978            var ssRules = stylesheet.cssRules || stylesheet.rules;
13979            for(var j = ssRules.length-1; j >= 0; --j){
13980                rules[ssRules[j].selectorText] = ssRules[j];
13981            }
13982        }catch(e){}
13983    },
13984    
13985    /**
13986     * Gets all css rules for the document
13987     * @param {Boolean} refreshCache true to refresh the internal cache
13988     * @return {Object} An object (hash) of rules indexed by selector
13989     */
13990    getRules : function(refreshCache){
13991                 if(rules == null || refreshCache){
13992                         rules = {};
13993                         var ds = doc.styleSheets;
13994                         for(var i =0, len = ds.length; i < len; i++){
13995                             try{
13996                         this.cacheStyleSheet(ds[i]);
13997                     }catch(e){} 
13998                 }
13999                 }
14000                 return rules;
14001         },
14002         
14003         /**
14004     * Gets an an individual CSS rule by selector(s)
14005     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14006     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14007     * @return {CSSRule} The CSS rule or null if one is not found
14008     */
14009    getRule : function(selector, refreshCache){
14010                 var rs = this.getRules(refreshCache);
14011                 if(!(selector instanceof Array)){
14012                     return rs[selector];
14013                 }
14014                 for(var i = 0; i < selector.length; i++){
14015                         if(rs[selector[i]]){
14016                                 return rs[selector[i]];
14017                         }
14018                 }
14019                 return null;
14020         },
14021         
14022         
14023         /**
14024     * Updates a rule property
14025     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14026     * @param {String} property The css property
14027     * @param {String} value The new value for the property
14028     * @return {Boolean} true If a rule was found and updated
14029     */
14030    updateRule : function(selector, property, value){
14031                 if(!(selector instanceof Array)){
14032                         var rule = this.getRule(selector);
14033                         if(rule){
14034                                 rule.style[property.replace(camelRe, camelFn)] = value;
14035                                 return true;
14036                         }
14037                 }else{
14038                         for(var i = 0; i < selector.length; i++){
14039                                 if(this.updateRule(selector[i], property, value)){
14040                                         return true;
14041                                 }
14042                         }
14043                 }
14044                 return false;
14045         }
14046    };   
14047 }();/*
14048  * Based on:
14049  * Ext JS Library 1.1.1
14050  * Copyright(c) 2006-2007, Ext JS, LLC.
14051  *
14052  * Originally Released Under LGPL - original licence link has changed is not relivant.
14053  *
14054  * Fork - LGPL
14055  * <script type="text/javascript">
14056  */
14057
14058  
14059
14060 /**
14061  * @class Roo.util.ClickRepeater
14062  * @extends Roo.util.Observable
14063  * 
14064  * A wrapper class which can be applied to any element. Fires a "click" event while the
14065  * mouse is pressed. The interval between firings may be specified in the config but
14066  * defaults to 10 milliseconds.
14067  * 
14068  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14069  * 
14070  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14071  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14072  * Similar to an autorepeat key delay.
14073  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14074  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14075  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14076  *           "interval" and "delay" are ignored. "immediate" is honored.
14077  * @cfg {Boolean} preventDefault True to prevent the default click event
14078  * @cfg {Boolean} stopDefault True to stop the default click event
14079  * 
14080  * @history
14081  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14082  *     2007-02-02 jvs Renamed to ClickRepeater
14083  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14084  *
14085  *  @constructor
14086  * @param {String/HTMLElement/Element} el The element to listen on
14087  * @param {Object} config
14088  **/
14089 Roo.util.ClickRepeater = function(el, config)
14090 {
14091     this.el = Roo.get(el);
14092     this.el.unselectable();
14093
14094     Roo.apply(this, config);
14095
14096     this.addEvents({
14097     /**
14098      * @event mousedown
14099      * Fires when the mouse button is depressed.
14100      * @param {Roo.util.ClickRepeater} this
14101      */
14102         "mousedown" : true,
14103     /**
14104      * @event click
14105      * Fires on a specified interval during the time the element is pressed.
14106      * @param {Roo.util.ClickRepeater} this
14107      */
14108         "click" : true,
14109     /**
14110      * @event mouseup
14111      * Fires when the mouse key is released.
14112      * @param {Roo.util.ClickRepeater} this
14113      */
14114         "mouseup" : true
14115     });
14116
14117     this.el.on("mousedown", this.handleMouseDown, this);
14118     if(this.preventDefault || this.stopDefault){
14119         this.el.on("click", function(e){
14120             if(this.preventDefault){
14121                 e.preventDefault();
14122             }
14123             if(this.stopDefault){
14124                 e.stopEvent();
14125             }
14126         }, this);
14127     }
14128
14129     // allow inline handler
14130     if(this.handler){
14131         this.on("click", this.handler,  this.scope || this);
14132     }
14133
14134     Roo.util.ClickRepeater.superclass.constructor.call(this);
14135 };
14136
14137 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14138     interval : 20,
14139     delay: 250,
14140     preventDefault : true,
14141     stopDefault : false,
14142     timer : 0,
14143
14144     // private
14145     handleMouseDown : function(){
14146         clearTimeout(this.timer);
14147         this.el.blur();
14148         if(this.pressClass){
14149             this.el.addClass(this.pressClass);
14150         }
14151         this.mousedownTime = new Date();
14152
14153         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14154         this.el.on("mouseout", this.handleMouseOut, this);
14155
14156         this.fireEvent("mousedown", this);
14157         this.fireEvent("click", this);
14158         
14159         this.timer = this.click.defer(this.delay || this.interval, this);
14160     },
14161
14162     // private
14163     click : function(){
14164         this.fireEvent("click", this);
14165         this.timer = this.click.defer(this.getInterval(), this);
14166     },
14167
14168     // private
14169     getInterval: function(){
14170         if(!this.accelerate){
14171             return this.interval;
14172         }
14173         var pressTime = this.mousedownTime.getElapsed();
14174         if(pressTime < 500){
14175             return 400;
14176         }else if(pressTime < 1700){
14177             return 320;
14178         }else if(pressTime < 2600){
14179             return 250;
14180         }else if(pressTime < 3500){
14181             return 180;
14182         }else if(pressTime < 4400){
14183             return 140;
14184         }else if(pressTime < 5300){
14185             return 80;
14186         }else if(pressTime < 6200){
14187             return 50;
14188         }else{
14189             return 10;
14190         }
14191     },
14192
14193     // private
14194     handleMouseOut : function(){
14195         clearTimeout(this.timer);
14196         if(this.pressClass){
14197             this.el.removeClass(this.pressClass);
14198         }
14199         this.el.on("mouseover", this.handleMouseReturn, this);
14200     },
14201
14202     // private
14203     handleMouseReturn : function(){
14204         this.el.un("mouseover", this.handleMouseReturn);
14205         if(this.pressClass){
14206             this.el.addClass(this.pressClass);
14207         }
14208         this.click();
14209     },
14210
14211     // private
14212     handleMouseUp : function(){
14213         clearTimeout(this.timer);
14214         this.el.un("mouseover", this.handleMouseReturn);
14215         this.el.un("mouseout", this.handleMouseOut);
14216         Roo.get(document).un("mouseup", this.handleMouseUp);
14217         this.el.removeClass(this.pressClass);
14218         this.fireEvent("mouseup", this);
14219     }
14220 });/*
14221  * Based on:
14222  * Ext JS Library 1.1.1
14223  * Copyright(c) 2006-2007, Ext JS, LLC.
14224  *
14225  * Originally Released Under LGPL - original licence link has changed is not relivant.
14226  *
14227  * Fork - LGPL
14228  * <script type="text/javascript">
14229  */
14230
14231  
14232 /**
14233  * @class Roo.KeyNav
14234  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14235  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14236  * way to implement custom navigation schemes for any UI component.</p>
14237  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14238  * pageUp, pageDown, del, home, end.  Usage:</p>
14239  <pre><code>
14240 var nav = new Roo.KeyNav("my-element", {
14241     "left" : function(e){
14242         this.moveLeft(e.ctrlKey);
14243     },
14244     "right" : function(e){
14245         this.moveRight(e.ctrlKey);
14246     },
14247     "enter" : function(e){
14248         this.save();
14249     },
14250     scope : this
14251 });
14252 </code></pre>
14253  * @constructor
14254  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14255  * @param {Object} config The config
14256  */
14257 Roo.KeyNav = function(el, config){
14258     this.el = Roo.get(el);
14259     Roo.apply(this, config);
14260     if(!this.disabled){
14261         this.disabled = true;
14262         this.enable();
14263     }
14264 };
14265
14266 Roo.KeyNav.prototype = {
14267     /**
14268      * @cfg {Boolean} disabled
14269      * True to disable this KeyNav instance (defaults to false)
14270      */
14271     disabled : false,
14272     /**
14273      * @cfg {String} defaultEventAction
14274      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14275      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14276      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14277      */
14278     defaultEventAction: "stopEvent",
14279     /**
14280      * @cfg {Boolean} forceKeyDown
14281      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14282      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14283      * handle keydown instead of keypress.
14284      */
14285     forceKeyDown : false,
14286
14287     // private
14288     prepareEvent : function(e){
14289         var k = e.getKey();
14290         var h = this.keyToHandler[k];
14291         //if(h && this[h]){
14292         //    e.stopPropagation();
14293         //}
14294         if(Roo.isSafari && h && k >= 37 && k <= 40){
14295             e.stopEvent();
14296         }
14297     },
14298
14299     // private
14300     relay : function(e){
14301         var k = e.getKey();
14302         var h = this.keyToHandler[k];
14303         if(h && this[h]){
14304             if(this.doRelay(e, this[h], h) !== true){
14305                 e[this.defaultEventAction]();
14306             }
14307         }
14308     },
14309
14310     // private
14311     doRelay : function(e, h, hname){
14312         return h.call(this.scope || this, e);
14313     },
14314
14315     // possible handlers
14316     enter : false,
14317     left : false,
14318     right : false,
14319     up : false,
14320     down : false,
14321     tab : false,
14322     esc : false,
14323     pageUp : false,
14324     pageDown : false,
14325     del : false,
14326     home : false,
14327     end : false,
14328
14329     // quick lookup hash
14330     keyToHandler : {
14331         37 : "left",
14332         39 : "right",
14333         38 : "up",
14334         40 : "down",
14335         33 : "pageUp",
14336         34 : "pageDown",
14337         46 : "del",
14338         36 : "home",
14339         35 : "end",
14340         13 : "enter",
14341         27 : "esc",
14342         9  : "tab"
14343     },
14344
14345         /**
14346          * Enable this KeyNav
14347          */
14348         enable: function(){
14349                 if(this.disabled){
14350             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14351             // the EventObject will normalize Safari automatically
14352             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14353                 this.el.on("keydown", this.relay,  this);
14354             }else{
14355                 this.el.on("keydown", this.prepareEvent,  this);
14356                 this.el.on("keypress", this.relay,  this);
14357             }
14358                     this.disabled = false;
14359                 }
14360         },
14361
14362         /**
14363          * Disable this KeyNav
14364          */
14365         disable: function(){
14366                 if(!this.disabled){
14367                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14368                 this.el.un("keydown", this.relay);
14369             }else{
14370                 this.el.un("keydown", this.prepareEvent);
14371                 this.el.un("keypress", this.relay);
14372             }
14373                     this.disabled = true;
14374                 }
14375         }
14376 };/*
14377  * Based on:
14378  * Ext JS Library 1.1.1
14379  * Copyright(c) 2006-2007, Ext JS, LLC.
14380  *
14381  * Originally Released Under LGPL - original licence link has changed is not relivant.
14382  *
14383  * Fork - LGPL
14384  * <script type="text/javascript">
14385  */
14386
14387  
14388 /**
14389  * @class Roo.KeyMap
14390  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14391  * The constructor accepts the same config object as defined by {@link #addBinding}.
14392  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14393  * combination it will call the function with this signature (if the match is a multi-key
14394  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14395  * A KeyMap can also handle a string representation of keys.<br />
14396  * Usage:
14397  <pre><code>
14398 // map one key by key code
14399 var map = new Roo.KeyMap("my-element", {
14400     key: 13, // or Roo.EventObject.ENTER
14401     fn: myHandler,
14402     scope: myObject
14403 });
14404
14405 // map multiple keys to one action by string
14406 var map = new Roo.KeyMap("my-element", {
14407     key: "a\r\n\t",
14408     fn: myHandler,
14409     scope: myObject
14410 });
14411
14412 // map multiple keys to multiple actions by strings and array of codes
14413 var map = new Roo.KeyMap("my-element", [
14414     {
14415         key: [10,13],
14416         fn: function(){ alert("Return was pressed"); }
14417     }, {
14418         key: "abc",
14419         fn: function(){ alert('a, b or c was pressed'); }
14420     }, {
14421         key: "\t",
14422         ctrl:true,
14423         shift:true,
14424         fn: function(){ alert('Control + shift + tab was pressed.'); }
14425     }
14426 ]);
14427 </code></pre>
14428  * <b>Note: A KeyMap starts enabled</b>
14429  * @constructor
14430  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14431  * @param {Object} config The config (see {@link #addBinding})
14432  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14433  */
14434 Roo.KeyMap = function(el, config, eventName){
14435     this.el  = Roo.get(el);
14436     this.eventName = eventName || "keydown";
14437     this.bindings = [];
14438     if(config){
14439         this.addBinding(config);
14440     }
14441     this.enable();
14442 };
14443
14444 Roo.KeyMap.prototype = {
14445     /**
14446      * True to stop the event from bubbling and prevent the default browser action if the
14447      * key was handled by the KeyMap (defaults to false)
14448      * @type Boolean
14449      */
14450     stopEvent : false,
14451
14452     /**
14453      * Add a new binding to this KeyMap. The following config object properties are supported:
14454      * <pre>
14455 Property    Type             Description
14456 ----------  ---------------  ----------------------------------------------------------------------
14457 key         String/Array     A single keycode or an array of keycodes to handle
14458 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14459 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14460 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14461 fn          Function         The function to call when KeyMap finds the expected key combination
14462 scope       Object           The scope of the callback function
14463 </pre>
14464      *
14465      * Usage:
14466      * <pre><code>
14467 // Create a KeyMap
14468 var map = new Roo.KeyMap(document, {
14469     key: Roo.EventObject.ENTER,
14470     fn: handleKey,
14471     scope: this
14472 });
14473
14474 //Add a new binding to the existing KeyMap later
14475 map.addBinding({
14476     key: 'abc',
14477     shift: true,
14478     fn: handleKey,
14479     scope: this
14480 });
14481 </code></pre>
14482      * @param {Object/Array} config A single KeyMap config or an array of configs
14483      */
14484         addBinding : function(config){
14485         if(config instanceof Array){
14486             for(var i = 0, len = config.length; i < len; i++){
14487                 this.addBinding(config[i]);
14488             }
14489             return;
14490         }
14491         var keyCode = config.key,
14492             shift = config.shift, 
14493             ctrl = config.ctrl, 
14494             alt = config.alt,
14495             fn = config.fn,
14496             scope = config.scope;
14497         if(typeof keyCode == "string"){
14498             var ks = [];
14499             var keyString = keyCode.toUpperCase();
14500             for(var j = 0, len = keyString.length; j < len; j++){
14501                 ks.push(keyString.charCodeAt(j));
14502             }
14503             keyCode = ks;
14504         }
14505         var keyArray = keyCode instanceof Array;
14506         var handler = function(e){
14507             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14508                 var k = e.getKey();
14509                 if(keyArray){
14510                     for(var i = 0, len = keyCode.length; i < len; i++){
14511                         if(keyCode[i] == k){
14512                           if(this.stopEvent){
14513                               e.stopEvent();
14514                           }
14515                           fn.call(scope || window, k, e);
14516                           return;
14517                         }
14518                     }
14519                 }else{
14520                     if(k == keyCode){
14521                         if(this.stopEvent){
14522                            e.stopEvent();
14523                         }
14524                         fn.call(scope || window, k, e);
14525                     }
14526                 }
14527             }
14528         };
14529         this.bindings.push(handler);  
14530         },
14531
14532     /**
14533      * Shorthand for adding a single key listener
14534      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14535      * following options:
14536      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14537      * @param {Function} fn The function to call
14538      * @param {Object} scope (optional) The scope of the function
14539      */
14540     on : function(key, fn, scope){
14541         var keyCode, shift, ctrl, alt;
14542         if(typeof key == "object" && !(key instanceof Array)){
14543             keyCode = key.key;
14544             shift = key.shift;
14545             ctrl = key.ctrl;
14546             alt = key.alt;
14547         }else{
14548             keyCode = key;
14549         }
14550         this.addBinding({
14551             key: keyCode,
14552             shift: shift,
14553             ctrl: ctrl,
14554             alt: alt,
14555             fn: fn,
14556             scope: scope
14557         })
14558     },
14559
14560     // private
14561     handleKeyDown : function(e){
14562             if(this.enabled){ //just in case
14563             var b = this.bindings;
14564             for(var i = 0, len = b.length; i < len; i++){
14565                 b[i].call(this, e);
14566             }
14567             }
14568         },
14569         
14570         /**
14571          * Returns true if this KeyMap is enabled
14572          * @return {Boolean} 
14573          */
14574         isEnabled : function(){
14575             return this.enabled;  
14576         },
14577         
14578         /**
14579          * Enables this KeyMap
14580          */
14581         enable: function(){
14582                 if(!this.enabled){
14583                     this.el.on(this.eventName, this.handleKeyDown, this);
14584                     this.enabled = true;
14585                 }
14586         },
14587
14588         /**
14589          * Disable this KeyMap
14590          */
14591         disable: function(){
14592                 if(this.enabled){
14593                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14594                     this.enabled = false;
14595                 }
14596         }
14597 };/*
14598  * Based on:
14599  * Ext JS Library 1.1.1
14600  * Copyright(c) 2006-2007, Ext JS, LLC.
14601  *
14602  * Originally Released Under LGPL - original licence link has changed is not relivant.
14603  *
14604  * Fork - LGPL
14605  * <script type="text/javascript">
14606  */
14607
14608  
14609 /**
14610  * @class Roo.util.TextMetrics
14611  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14612  * wide, in pixels, a given block of text will be.
14613  * @singleton
14614  */
14615 Roo.util.TextMetrics = function(){
14616     var shared;
14617     return {
14618         /**
14619          * Measures the size of the specified text
14620          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14621          * that can affect the size of the rendered text
14622          * @param {String} text The text to measure
14623          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14624          * in order to accurately measure the text height
14625          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14626          */
14627         measure : function(el, text, fixedWidth){
14628             if(!shared){
14629                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14630             }
14631             shared.bind(el);
14632             shared.setFixedWidth(fixedWidth || 'auto');
14633             return shared.getSize(text);
14634         },
14635
14636         /**
14637          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14638          * the overhead of multiple calls to initialize the style properties on each measurement.
14639          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14640          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14641          * in order to accurately measure the text height
14642          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14643          */
14644         createInstance : function(el, fixedWidth){
14645             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14646         }
14647     };
14648 }();
14649
14650  
14651
14652 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14653     var ml = new Roo.Element(document.createElement('div'));
14654     document.body.appendChild(ml.dom);
14655     ml.position('absolute');
14656     ml.setLeftTop(-1000, -1000);
14657     ml.hide();
14658
14659     if(fixedWidth){
14660         ml.setWidth(fixedWidth);
14661     }
14662      
14663     var instance = {
14664         /**
14665          * Returns the size of the specified text based on the internal element's style and width properties
14666          * @memberOf Roo.util.TextMetrics.Instance#
14667          * @param {String} text The text to measure
14668          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14669          */
14670         getSize : function(text){
14671             ml.update(text);
14672             var s = ml.getSize();
14673             ml.update('');
14674             return s;
14675         },
14676
14677         /**
14678          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14679          * that can affect the size of the rendered text
14680          * @memberOf Roo.util.TextMetrics.Instance#
14681          * @param {String/HTMLElement} el The element, dom node or id
14682          */
14683         bind : function(el){
14684             ml.setStyle(
14685                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14686             );
14687         },
14688
14689         /**
14690          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14691          * to set a fixed width in order to accurately measure the text height.
14692          * @memberOf Roo.util.TextMetrics.Instance#
14693          * @param {Number} width The width to set on the element
14694          */
14695         setFixedWidth : function(width){
14696             ml.setWidth(width);
14697         },
14698
14699         /**
14700          * Returns the measured width of the specified text
14701          * @memberOf Roo.util.TextMetrics.Instance#
14702          * @param {String} text The text to measure
14703          * @return {Number} width The width in pixels
14704          */
14705         getWidth : function(text){
14706             ml.dom.style.width = 'auto';
14707             return this.getSize(text).width;
14708         },
14709
14710         /**
14711          * Returns the measured height of the specified text.  For multiline text, be sure to call
14712          * {@link #setFixedWidth} if necessary.
14713          * @memberOf Roo.util.TextMetrics.Instance#
14714          * @param {String} text The text to measure
14715          * @return {Number} height The height in pixels
14716          */
14717         getHeight : function(text){
14718             return this.getSize(text).height;
14719         }
14720     };
14721
14722     instance.bind(bindTo);
14723
14724     return instance;
14725 };
14726
14727 // backwards compat
14728 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14729  * Based on:
14730  * Ext JS Library 1.1.1
14731  * Copyright(c) 2006-2007, Ext JS, LLC.
14732  *
14733  * Originally Released Under LGPL - original licence link has changed is not relivant.
14734  *
14735  * Fork - LGPL
14736  * <script type="text/javascript">
14737  */
14738
14739 /**
14740  * @class Roo.state.Provider
14741  * Abstract base class for state provider implementations. This class provides methods
14742  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14743  * Provider interface.
14744  */
14745 Roo.state.Provider = function(){
14746     /**
14747      * @event statechange
14748      * Fires when a state change occurs.
14749      * @param {Provider} this This state provider
14750      * @param {String} key The state key which was changed
14751      * @param {String} value The encoded value for the state
14752      */
14753     this.addEvents({
14754         "statechange": true
14755     });
14756     this.state = {};
14757     Roo.state.Provider.superclass.constructor.call(this);
14758 };
14759 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14760     /**
14761      * Returns the current value for a key
14762      * @param {String} name The key name
14763      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14764      * @return {Mixed} The state data
14765      */
14766     get : function(name, defaultValue){
14767         return typeof this.state[name] == "undefined" ?
14768             defaultValue : this.state[name];
14769     },
14770     
14771     /**
14772      * Clears a value from the state
14773      * @param {String} name The key name
14774      */
14775     clear : function(name){
14776         delete this.state[name];
14777         this.fireEvent("statechange", this, name, null);
14778     },
14779     
14780     /**
14781      * Sets the value for a key
14782      * @param {String} name The key name
14783      * @param {Mixed} value The value to set
14784      */
14785     set : function(name, value){
14786         this.state[name] = value;
14787         this.fireEvent("statechange", this, name, value);
14788     },
14789     
14790     /**
14791      * Decodes a string previously encoded with {@link #encodeValue}.
14792      * @param {String} value The value to decode
14793      * @return {Mixed} The decoded value
14794      */
14795     decodeValue : function(cookie){
14796         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14797         var matches = re.exec(unescape(cookie));
14798         if(!matches || !matches[1]) return; // non state cookie
14799         var type = matches[1];
14800         var v = matches[2];
14801         switch(type){
14802             case "n":
14803                 return parseFloat(v);
14804             case "d":
14805                 return new Date(Date.parse(v));
14806             case "b":
14807                 return (v == "1");
14808             case "a":
14809                 var all = [];
14810                 var values = v.split("^");
14811                 for(var i = 0, len = values.length; i < len; i++){
14812                     all.push(this.decodeValue(values[i]));
14813                 }
14814                 return all;
14815            case "o":
14816                 var all = {};
14817                 var values = v.split("^");
14818                 for(var i = 0, len = values.length; i < len; i++){
14819                     var kv = values[i].split("=");
14820                     all[kv[0]] = this.decodeValue(kv[1]);
14821                 }
14822                 return all;
14823            default:
14824                 return v;
14825         }
14826     },
14827     
14828     /**
14829      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14830      * @param {Mixed} value The value to encode
14831      * @return {String} The encoded value
14832      */
14833     encodeValue : function(v){
14834         var enc;
14835         if(typeof v == "number"){
14836             enc = "n:" + v;
14837         }else if(typeof v == "boolean"){
14838             enc = "b:" + (v ? "1" : "0");
14839         }else if(v instanceof Date){
14840             enc = "d:" + v.toGMTString();
14841         }else if(v instanceof Array){
14842             var flat = "";
14843             for(var i = 0, len = v.length; i < len; i++){
14844                 flat += this.encodeValue(v[i]);
14845                 if(i != len-1) flat += "^";
14846             }
14847             enc = "a:" + flat;
14848         }else if(typeof v == "object"){
14849             var flat = "";
14850             for(var key in v){
14851                 if(typeof v[key] != "function"){
14852                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14853                 }
14854             }
14855             enc = "o:" + flat.substring(0, flat.length-1);
14856         }else{
14857             enc = "s:" + v;
14858         }
14859         return escape(enc);        
14860     }
14861 });
14862
14863 /*
14864  * Based on:
14865  * Ext JS Library 1.1.1
14866  * Copyright(c) 2006-2007, Ext JS, LLC.
14867  *
14868  * Originally Released Under LGPL - original licence link has changed is not relivant.
14869  *
14870  * Fork - LGPL
14871  * <script type="text/javascript">
14872  */
14873 /**
14874  * @class Roo.state.Manager
14875  * This is the global state manager. By default all components that are "state aware" check this class
14876  * for state information if you don't pass them a custom state provider. In order for this class
14877  * to be useful, it must be initialized with a provider when your application initializes.
14878  <pre><code>
14879 // in your initialization function
14880 init : function(){
14881    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14882    ...
14883    // supposed you have a {@link Roo.BorderLayout}
14884    var layout = new Roo.BorderLayout(...);
14885    layout.restoreState();
14886    // or a {Roo.BasicDialog}
14887    var dialog = new Roo.BasicDialog(...);
14888    dialog.restoreState();
14889  </code></pre>
14890  * @singleton
14891  */
14892 Roo.state.Manager = function(){
14893     var provider = new Roo.state.Provider();
14894     
14895     return {
14896         /**
14897          * Configures the default state provider for your application
14898          * @param {Provider} stateProvider The state provider to set
14899          */
14900         setProvider : function(stateProvider){
14901             provider = stateProvider;
14902         },
14903         
14904         /**
14905          * Returns the current value for a key
14906          * @param {String} name The key name
14907          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14908          * @return {Mixed} The state data
14909          */
14910         get : function(key, defaultValue){
14911             return provider.get(key, defaultValue);
14912         },
14913         
14914         /**
14915          * Sets the value for a key
14916          * @param {String} name The key name
14917          * @param {Mixed} value The state data
14918          */
14919          set : function(key, value){
14920             provider.set(key, value);
14921         },
14922         
14923         /**
14924          * Clears a value from the state
14925          * @param {String} name The key name
14926          */
14927         clear : function(key){
14928             provider.clear(key);
14929         },
14930         
14931         /**
14932          * Gets the currently configured state provider
14933          * @return {Provider} The state provider
14934          */
14935         getProvider : function(){
14936             return provider;
14937         }
14938     };
14939 }();
14940 /*
14941  * Based on:
14942  * Ext JS Library 1.1.1
14943  * Copyright(c) 2006-2007, Ext JS, LLC.
14944  *
14945  * Originally Released Under LGPL - original licence link has changed is not relivant.
14946  *
14947  * Fork - LGPL
14948  * <script type="text/javascript">
14949  */
14950 /**
14951  * @class Roo.state.CookieProvider
14952  * @extends Roo.state.Provider
14953  * The default Provider implementation which saves state via cookies.
14954  * <br />Usage:
14955  <pre><code>
14956    var cp = new Roo.state.CookieProvider({
14957        path: "/cgi-bin/",
14958        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14959        domain: "roojs.com"
14960    })
14961    Roo.state.Manager.setProvider(cp);
14962  </code></pre>
14963  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14964  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14965  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14966  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14967  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14968  * domain the page is running on including the 'www' like 'www.roojs.com')
14969  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14970  * @constructor
14971  * Create a new CookieProvider
14972  * @param {Object} config The configuration object
14973  */
14974 Roo.state.CookieProvider = function(config){
14975     Roo.state.CookieProvider.superclass.constructor.call(this);
14976     this.path = "/";
14977     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14978     this.domain = null;
14979     this.secure = false;
14980     Roo.apply(this, config);
14981     this.state = this.readCookies();
14982 };
14983
14984 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14985     // private
14986     set : function(name, value){
14987         if(typeof value == "undefined" || value === null){
14988             this.clear(name);
14989             return;
14990         }
14991         this.setCookie(name, value);
14992         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14993     },
14994
14995     // private
14996     clear : function(name){
14997         this.clearCookie(name);
14998         Roo.state.CookieProvider.superclass.clear.call(this, name);
14999     },
15000
15001     // private
15002     readCookies : function(){
15003         var cookies = {};
15004         var c = document.cookie + ";";
15005         var re = /\s?(.*?)=(.*?);/g;
15006         var matches;
15007         while((matches = re.exec(c)) != null){
15008             var name = matches[1];
15009             var value = matches[2];
15010             if(name && name.substring(0,3) == "ys-"){
15011                 cookies[name.substr(3)] = this.decodeValue(value);
15012             }
15013         }
15014         return cookies;
15015     },
15016
15017     // private
15018     setCookie : function(name, value){
15019         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15020            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15021            ((this.path == null) ? "" : ("; path=" + this.path)) +
15022            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15023            ((this.secure == true) ? "; secure" : "");
15024     },
15025
15026     // private
15027     clearCookie : function(name){
15028         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15029            ((this.path == null) ? "" : ("; path=" + this.path)) +
15030            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15031            ((this.secure == true) ? "; secure" : "");
15032     }
15033 });/*
15034  * Based on:
15035  * Ext JS Library 1.1.1
15036  * Copyright(c) 2006-2007, Ext JS, LLC.
15037  *
15038  * Originally Released Under LGPL - original licence link has changed is not relivant.
15039  *
15040  * Fork - LGPL
15041  * <script type="text/javascript">
15042  */
15043  
15044
15045 /**
15046  * @class Roo.ComponentMgr
15047  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15048  * @singleton
15049  */
15050 Roo.ComponentMgr = function(){
15051     var all = new Roo.util.MixedCollection();
15052
15053     return {
15054         /**
15055          * Registers a component.
15056          * @param {Roo.Component} c The component
15057          */
15058         register : function(c){
15059             all.add(c);
15060         },
15061
15062         /**
15063          * Unregisters a component.
15064          * @param {Roo.Component} c The component
15065          */
15066         unregister : function(c){
15067             all.remove(c);
15068         },
15069
15070         /**
15071          * Returns a component by id
15072          * @param {String} id The component id
15073          */
15074         get : function(id){
15075             return all.get(id);
15076         },
15077
15078         /**
15079          * Registers a function that will be called when a specified component is added to ComponentMgr
15080          * @param {String} id The component id
15081          * @param {Funtction} fn The callback function
15082          * @param {Object} scope The scope of the callback
15083          */
15084         onAvailable : function(id, fn, scope){
15085             all.on("add", function(index, o){
15086                 if(o.id == id){
15087                     fn.call(scope || o, o);
15088                     all.un("add", fn, scope);
15089                 }
15090             });
15091         }
15092     };
15093 }();/*
15094  * Based on:
15095  * Ext JS Library 1.1.1
15096  * Copyright(c) 2006-2007, Ext JS, LLC.
15097  *
15098  * Originally Released Under LGPL - original licence link has changed is not relivant.
15099  *
15100  * Fork - LGPL
15101  * <script type="text/javascript">
15102  */
15103  
15104 /**
15105  * @class Roo.Component
15106  * @extends Roo.util.Observable
15107  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15108  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15109  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15110  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15111  * All visual components (widgets) that require rendering into a layout should subclass Component.
15112  * @constructor
15113  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15114  * 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
15115  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15116  */
15117 Roo.Component = function(config){
15118     config = config || {};
15119     if(config.tagName || config.dom || typeof config == "string"){ // element object
15120         config = {el: config, id: config.id || config};
15121     }
15122     this.initialConfig = config;
15123
15124     Roo.apply(this, config);
15125     this.addEvents({
15126         /**
15127          * @event disable
15128          * Fires after the component is disabled.
15129              * @param {Roo.Component} this
15130              */
15131         disable : true,
15132         /**
15133          * @event enable
15134          * Fires after the component is enabled.
15135              * @param {Roo.Component} this
15136              */
15137         enable : true,
15138         /**
15139          * @event beforeshow
15140          * Fires before the component is shown.  Return false to stop the show.
15141              * @param {Roo.Component} this
15142              */
15143         beforeshow : true,
15144         /**
15145          * @event show
15146          * Fires after the component is shown.
15147              * @param {Roo.Component} this
15148              */
15149         show : true,
15150         /**
15151          * @event beforehide
15152          * Fires before the component is hidden. Return false to stop the hide.
15153              * @param {Roo.Component} this
15154              */
15155         beforehide : true,
15156         /**
15157          * @event hide
15158          * Fires after the component is hidden.
15159              * @param {Roo.Component} this
15160              */
15161         hide : true,
15162         /**
15163          * @event beforerender
15164          * Fires before the component is rendered. Return false to stop the render.
15165              * @param {Roo.Component} this
15166              */
15167         beforerender : true,
15168         /**
15169          * @event render
15170          * Fires after the component is rendered.
15171              * @param {Roo.Component} this
15172              */
15173         render : true,
15174         /**
15175          * @event beforedestroy
15176          * Fires before the component is destroyed. Return false to stop the destroy.
15177              * @param {Roo.Component} this
15178              */
15179         beforedestroy : true,
15180         /**
15181          * @event destroy
15182          * Fires after the component is destroyed.
15183              * @param {Roo.Component} this
15184              */
15185         destroy : true
15186     });
15187     if(!this.id){
15188         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15189     }
15190     Roo.ComponentMgr.register(this);
15191     Roo.Component.superclass.constructor.call(this);
15192     this.initComponent();
15193     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15194         this.render(this.renderTo);
15195         delete this.renderTo;
15196     }
15197 };
15198
15199 /** @private */
15200 Roo.Component.AUTO_ID = 1000;
15201
15202 Roo.extend(Roo.Component, Roo.util.Observable, {
15203     /**
15204      * @scope Roo.Component.prototype
15205      * @type {Boolean}
15206      * true if this component is hidden. Read-only.
15207      */
15208     hidden : false,
15209     /**
15210      * @type {Boolean}
15211      * true if this component is disabled. Read-only.
15212      */
15213     disabled : false,
15214     /**
15215      * @type {Boolean}
15216      * true if this component has been rendered. Read-only.
15217      */
15218     rendered : false,
15219     
15220     /** @cfg {String} disableClass
15221      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15222      */
15223     disabledClass : "x-item-disabled",
15224         /** @cfg {Boolean} allowDomMove
15225          * Whether the component can move the Dom node when rendering (defaults to true).
15226          */
15227     allowDomMove : true,
15228     /** @cfg {String} hideMode (display|visibility)
15229      * How this component should hidden. Supported values are
15230      * "visibility" (css visibility), "offsets" (negative offset position) and
15231      * "display" (css display) - defaults to "display".
15232      */
15233     hideMode: 'display',
15234
15235     /** @private */
15236     ctype : "Roo.Component",
15237
15238     /**
15239      * @cfg {String} actionMode 
15240      * which property holds the element that used for  hide() / show() / disable() / enable()
15241      * default is 'el' 
15242      */
15243     actionMode : "el",
15244
15245     /** @private */
15246     getActionEl : function(){
15247         return this[this.actionMode];
15248     },
15249
15250     initComponent : Roo.emptyFn,
15251     /**
15252      * If this is a lazy rendering component, render it to its container element.
15253      * @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.
15254      */
15255     render : function(container, position){
15256         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15257             if(!container && this.el){
15258                 this.el = Roo.get(this.el);
15259                 container = this.el.dom.parentNode;
15260                 this.allowDomMove = false;
15261             }
15262             this.container = Roo.get(container);
15263             this.rendered = true;
15264             if(position !== undefined){
15265                 if(typeof position == 'number'){
15266                     position = this.container.dom.childNodes[position];
15267                 }else{
15268                     position = Roo.getDom(position);
15269                 }
15270             }
15271             this.onRender(this.container, position || null);
15272             if(this.cls){
15273                 this.el.addClass(this.cls);
15274                 delete this.cls;
15275             }
15276             if(this.style){
15277                 this.el.applyStyles(this.style);
15278                 delete this.style;
15279             }
15280             this.fireEvent("render", this);
15281             this.afterRender(this.container);
15282             if(this.hidden){
15283                 this.hide();
15284             }
15285             if(this.disabled){
15286                 this.disable();
15287             }
15288         }
15289         return this;
15290     },
15291
15292     /** @private */
15293     // default function is not really useful
15294     onRender : function(ct, position){
15295         if(this.el){
15296             this.el = Roo.get(this.el);
15297             if(this.allowDomMove !== false){
15298                 ct.dom.insertBefore(this.el.dom, position);
15299             }
15300         }
15301     },
15302
15303     /** @private */
15304     getAutoCreate : function(){
15305         var cfg = typeof this.autoCreate == "object" ?
15306                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15307         if(this.id && !cfg.id){
15308             cfg.id = this.id;
15309         }
15310         return cfg;
15311     },
15312
15313     /** @private */
15314     afterRender : Roo.emptyFn,
15315
15316     /**
15317      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15318      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15319      */
15320     destroy : function(){
15321         if(this.fireEvent("beforedestroy", this) !== false){
15322             this.purgeListeners();
15323             this.beforeDestroy();
15324             if(this.rendered){
15325                 this.el.removeAllListeners();
15326                 this.el.remove();
15327                 if(this.actionMode == "container"){
15328                     this.container.remove();
15329                 }
15330             }
15331             this.onDestroy();
15332             Roo.ComponentMgr.unregister(this);
15333             this.fireEvent("destroy", this);
15334         }
15335     },
15336
15337         /** @private */
15338     beforeDestroy : function(){
15339
15340     },
15341
15342         /** @private */
15343         onDestroy : function(){
15344
15345     },
15346
15347     /**
15348      * Returns the underlying {@link Roo.Element}.
15349      * @return {Roo.Element} The element
15350      */
15351     getEl : function(){
15352         return this.el;
15353     },
15354
15355     /**
15356      * Returns the id of this component.
15357      * @return {String}
15358      */
15359     getId : function(){
15360         return this.id;
15361     },
15362
15363     /**
15364      * Try to focus this component.
15365      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15366      * @return {Roo.Component} this
15367      */
15368     focus : function(selectText){
15369         if(this.rendered){
15370             this.el.focus();
15371             if(selectText === true){
15372                 this.el.dom.select();
15373             }
15374         }
15375         return this;
15376     },
15377
15378     /** @private */
15379     blur : function(){
15380         if(this.rendered){
15381             this.el.blur();
15382         }
15383         return this;
15384     },
15385
15386     /**
15387      * Disable this component.
15388      * @return {Roo.Component} this
15389      */
15390     disable : function(){
15391         if(this.rendered){
15392             this.onDisable();
15393         }
15394         this.disabled = true;
15395         this.fireEvent("disable", this);
15396         return this;
15397     },
15398
15399         // private
15400     onDisable : function(){
15401         this.getActionEl().addClass(this.disabledClass);
15402         this.el.dom.disabled = true;
15403     },
15404
15405     /**
15406      * Enable this component.
15407      * @return {Roo.Component} this
15408      */
15409     enable : function(){
15410         if(this.rendered){
15411             this.onEnable();
15412         }
15413         this.disabled = false;
15414         this.fireEvent("enable", this);
15415         return this;
15416     },
15417
15418         // private
15419     onEnable : function(){
15420         this.getActionEl().removeClass(this.disabledClass);
15421         this.el.dom.disabled = false;
15422     },
15423
15424     /**
15425      * Convenience function for setting disabled/enabled by boolean.
15426      * @param {Boolean} disabled
15427      */
15428     setDisabled : function(disabled){
15429         this[disabled ? "disable" : "enable"]();
15430     },
15431
15432     /**
15433      * Show this component.
15434      * @return {Roo.Component} this
15435      */
15436     show: function(){
15437         if(this.fireEvent("beforeshow", this) !== false){
15438             this.hidden = false;
15439             if(this.rendered){
15440                 this.onShow();
15441             }
15442             this.fireEvent("show", this);
15443         }
15444         return this;
15445     },
15446
15447     // private
15448     onShow : function(){
15449         var ae = this.getActionEl();
15450         if(this.hideMode == 'visibility'){
15451             ae.dom.style.visibility = "visible";
15452         }else if(this.hideMode == 'offsets'){
15453             ae.removeClass('x-hidden');
15454         }else{
15455             ae.dom.style.display = "";
15456         }
15457     },
15458
15459     /**
15460      * Hide this component.
15461      * @return {Roo.Component} this
15462      */
15463     hide: function(){
15464         if(this.fireEvent("beforehide", this) !== false){
15465             this.hidden = true;
15466             if(this.rendered){
15467                 this.onHide();
15468             }
15469             this.fireEvent("hide", this);
15470         }
15471         return this;
15472     },
15473
15474     // private
15475     onHide : function(){
15476         var ae = this.getActionEl();
15477         if(this.hideMode == 'visibility'){
15478             ae.dom.style.visibility = "hidden";
15479         }else if(this.hideMode == 'offsets'){
15480             ae.addClass('x-hidden');
15481         }else{
15482             ae.dom.style.display = "none";
15483         }
15484     },
15485
15486     /**
15487      * Convenience function to hide or show this component by boolean.
15488      * @param {Boolean} visible True to show, false to hide
15489      * @return {Roo.Component} this
15490      */
15491     setVisible: function(visible){
15492         if(visible) {
15493             this.show();
15494         }else{
15495             this.hide();
15496         }
15497         return this;
15498     },
15499
15500     /**
15501      * Returns true if this component is visible.
15502      */
15503     isVisible : function(){
15504         return this.getActionEl().isVisible();
15505     },
15506
15507     cloneConfig : function(overrides){
15508         overrides = overrides || {};
15509         var id = overrides.id || Roo.id();
15510         var cfg = Roo.applyIf(overrides, this.initialConfig);
15511         cfg.id = id; // prevent dup id
15512         return new this.constructor(cfg);
15513     }
15514 });/*
15515  * Based on:
15516  * Ext JS Library 1.1.1
15517  * Copyright(c) 2006-2007, Ext JS, LLC.
15518  *
15519  * Originally Released Under LGPL - original licence link has changed is not relivant.
15520  *
15521  * Fork - LGPL
15522  * <script type="text/javascript">
15523  */
15524
15525 /**
15526  * @class Roo.BoxComponent
15527  * @extends Roo.Component
15528  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15529  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15530  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15531  * layout containers.
15532  * @constructor
15533  * @param {Roo.Element/String/Object} config The configuration options.
15534  */
15535 Roo.BoxComponent = function(config){
15536     Roo.Component.call(this, config);
15537     this.addEvents({
15538         /**
15539          * @event resize
15540          * Fires after the component is resized.
15541              * @param {Roo.Component} this
15542              * @param {Number} adjWidth The box-adjusted width that was set
15543              * @param {Number} adjHeight The box-adjusted height that was set
15544              * @param {Number} rawWidth The width that was originally specified
15545              * @param {Number} rawHeight The height that was originally specified
15546              */
15547         resize : true,
15548         /**
15549          * @event move
15550          * Fires after the component is moved.
15551              * @param {Roo.Component} this
15552              * @param {Number} x The new x position
15553              * @param {Number} y The new y position
15554              */
15555         move : true
15556     });
15557 };
15558
15559 Roo.extend(Roo.BoxComponent, Roo.Component, {
15560     // private, set in afterRender to signify that the component has been rendered
15561     boxReady : false,
15562     // private, used to defer height settings to subclasses
15563     deferHeight: false,
15564     /** @cfg {Number} width
15565      * width (optional) size of component
15566      */
15567      /** @cfg {Number} height
15568      * height (optional) size of component
15569      */
15570      
15571     /**
15572      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15573      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15574      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15575      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15576      * @return {Roo.BoxComponent} this
15577      */
15578     setSize : function(w, h){
15579         // support for standard size objects
15580         if(typeof w == 'object'){
15581             h = w.height;
15582             w = w.width;
15583         }
15584         // not rendered
15585         if(!this.boxReady){
15586             this.width = w;
15587             this.height = h;
15588             return this;
15589         }
15590
15591         // prevent recalcs when not needed
15592         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15593             return this;
15594         }
15595         this.lastSize = {width: w, height: h};
15596
15597         var adj = this.adjustSize(w, h);
15598         var aw = adj.width, ah = adj.height;
15599         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15600             var rz = this.getResizeEl();
15601             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15602                 rz.setSize(aw, ah);
15603             }else if(!this.deferHeight && ah !== undefined){
15604                 rz.setHeight(ah);
15605             }else if(aw !== undefined){
15606                 rz.setWidth(aw);
15607             }
15608             this.onResize(aw, ah, w, h);
15609             this.fireEvent('resize', this, aw, ah, w, h);
15610         }
15611         return this;
15612     },
15613
15614     /**
15615      * Gets the current size of the component's underlying element.
15616      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15617      */
15618     getSize : function(){
15619         return this.el.getSize();
15620     },
15621
15622     /**
15623      * Gets the current XY position of the component's underlying element.
15624      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15625      * @return {Array} The XY position of the element (e.g., [100, 200])
15626      */
15627     getPosition : function(local){
15628         if(local === true){
15629             return [this.el.getLeft(true), this.el.getTop(true)];
15630         }
15631         return this.xy || this.el.getXY();
15632     },
15633
15634     /**
15635      * Gets the current box measurements of the component's underlying element.
15636      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15637      * @returns {Object} box An object in the format {x, y, width, height}
15638      */
15639     getBox : function(local){
15640         var s = this.el.getSize();
15641         if(local){
15642             s.x = this.el.getLeft(true);
15643             s.y = this.el.getTop(true);
15644         }else{
15645             var xy = this.xy || this.el.getXY();
15646             s.x = xy[0];
15647             s.y = xy[1];
15648         }
15649         return s;
15650     },
15651
15652     /**
15653      * Sets the current box measurements of the component's underlying element.
15654      * @param {Object} box An object in the format {x, y, width, height}
15655      * @returns {Roo.BoxComponent} this
15656      */
15657     updateBox : function(box){
15658         this.setSize(box.width, box.height);
15659         this.setPagePosition(box.x, box.y);
15660         return this;
15661     },
15662
15663     // protected
15664     getResizeEl : function(){
15665         return this.resizeEl || this.el;
15666     },
15667
15668     // protected
15669     getPositionEl : function(){
15670         return this.positionEl || this.el;
15671     },
15672
15673     /**
15674      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15675      * This method fires the move event.
15676      * @param {Number} left The new left
15677      * @param {Number} top The new top
15678      * @returns {Roo.BoxComponent} this
15679      */
15680     setPosition : function(x, y){
15681         this.x = x;
15682         this.y = y;
15683         if(!this.boxReady){
15684             return this;
15685         }
15686         var adj = this.adjustPosition(x, y);
15687         var ax = adj.x, ay = adj.y;
15688
15689         var el = this.getPositionEl();
15690         if(ax !== undefined || ay !== undefined){
15691             if(ax !== undefined && ay !== undefined){
15692                 el.setLeftTop(ax, ay);
15693             }else if(ax !== undefined){
15694                 el.setLeft(ax);
15695             }else if(ay !== undefined){
15696                 el.setTop(ay);
15697             }
15698             this.onPosition(ax, ay);
15699             this.fireEvent('move', this, ax, ay);
15700         }
15701         return this;
15702     },
15703
15704     /**
15705      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15706      * This method fires the move event.
15707      * @param {Number} x The new x position
15708      * @param {Number} y The new y position
15709      * @returns {Roo.BoxComponent} this
15710      */
15711     setPagePosition : function(x, y){
15712         this.pageX = x;
15713         this.pageY = y;
15714         if(!this.boxReady){
15715             return;
15716         }
15717         if(x === undefined || y === undefined){ // cannot translate undefined points
15718             return;
15719         }
15720         var p = this.el.translatePoints(x, y);
15721         this.setPosition(p.left, p.top);
15722         return this;
15723     },
15724
15725     // private
15726     onRender : function(ct, position){
15727         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15728         if(this.resizeEl){
15729             this.resizeEl = Roo.get(this.resizeEl);
15730         }
15731         if(this.positionEl){
15732             this.positionEl = Roo.get(this.positionEl);
15733         }
15734     },
15735
15736     // private
15737     afterRender : function(){
15738         Roo.BoxComponent.superclass.afterRender.call(this);
15739         this.boxReady = true;
15740         this.setSize(this.width, this.height);
15741         if(this.x || this.y){
15742             this.setPosition(this.x, this.y);
15743         }
15744         if(this.pageX || this.pageY){
15745             this.setPagePosition(this.pageX, this.pageY);
15746         }
15747     },
15748
15749     /**
15750      * Force the component's size to recalculate based on the underlying element's current height and width.
15751      * @returns {Roo.BoxComponent} this
15752      */
15753     syncSize : function(){
15754         delete this.lastSize;
15755         this.setSize(this.el.getWidth(), this.el.getHeight());
15756         return this;
15757     },
15758
15759     /**
15760      * Called after the component is resized, this method is empty by default but can be implemented by any
15761      * subclass that needs to perform custom logic after a resize occurs.
15762      * @param {Number} adjWidth The box-adjusted width that was set
15763      * @param {Number} adjHeight The box-adjusted height that was set
15764      * @param {Number} rawWidth The width that was originally specified
15765      * @param {Number} rawHeight The height that was originally specified
15766      */
15767     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15768
15769     },
15770
15771     /**
15772      * Called after the component is moved, this method is empty by default but can be implemented by any
15773      * subclass that needs to perform custom logic after a move occurs.
15774      * @param {Number} x The new x position
15775      * @param {Number} y The new y position
15776      */
15777     onPosition : function(x, y){
15778
15779     },
15780
15781     // private
15782     adjustSize : function(w, h){
15783         if(this.autoWidth){
15784             w = 'auto';
15785         }
15786         if(this.autoHeight){
15787             h = 'auto';
15788         }
15789         return {width : w, height: h};
15790     },
15791
15792     // private
15793     adjustPosition : function(x, y){
15794         return {x : x, y: y};
15795     }
15796 });/*
15797  * Original code for Roojs - LGPL
15798  * <script type="text/javascript">
15799  */
15800  
15801 /**
15802  * @class Roo.XComponent
15803  * A delayed Element creator...
15804  * Or a way to group chunks of interface together.
15805  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15806  *  used in conjunction with XComponent.build() it will create an instance of each element,
15807  *  then call addxtype() to build the User interface.
15808  * 
15809  * Mypart.xyx = new Roo.XComponent({
15810
15811     parent : 'Mypart.xyz', // empty == document.element.!!
15812     order : '001',
15813     name : 'xxxx'
15814     region : 'xxxx'
15815     disabled : function() {} 
15816      
15817     tree : function() { // return an tree of xtype declared components
15818         var MODULE = this;
15819         return 
15820         {
15821             xtype : 'NestedLayoutPanel',
15822             // technicall
15823         }
15824      ]
15825  *})
15826  *
15827  *
15828  * It can be used to build a big heiracy, with parent etc.
15829  * or you can just use this to render a single compoent to a dom element
15830  * MYPART.render(Roo.Element | String(id) | dom_element )
15831  *
15832  *
15833  * Usage patterns.
15834  *
15835  * Classic Roo
15836  *
15837  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15838  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15839  *
15840  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15841  *
15842  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15843  * - if mulitple topModules exist, the last one is defined as the top module.
15844  *
15845  * Embeded Roo
15846  * 
15847  * When the top level or multiple modules are to embedded into a existing HTML page,
15848  * the parent element can container '#id' of the element where the module will be drawn.
15849  *
15850  * Bootstrap Roo
15851  *
15852  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15853  * it relies more on a include mechanism, where sub modules are included into an outer page.
15854  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15855  * 
15856  * Bootstrap Roo Included elements
15857  *
15858  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15859  * hence confusing the component builder as it thinks there are multiple top level elements. 
15860  *
15861  * 
15862  * 
15863  * @extends Roo.util.Observable
15864  * @constructor
15865  * @param cfg {Object} configuration of component
15866  * 
15867  */
15868 Roo.XComponent = function(cfg) {
15869     Roo.apply(this, cfg);
15870     this.addEvents({ 
15871         /**
15872              * @event built
15873              * Fires when this the componnt is built
15874              * @param {Roo.XComponent} c the component
15875              */
15876         'built' : true
15877         
15878     });
15879     this.region = this.region || 'center'; // default..
15880     Roo.XComponent.register(this);
15881     this.modules = false;
15882     this.el = false; // where the layout goes..
15883     
15884     
15885 }
15886 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15887     /**
15888      * @property el
15889      * The created element (with Roo.factory())
15890      * @type {Roo.Layout}
15891      */
15892     el  : false,
15893     
15894     /**
15895      * @property el
15896      * for BC  - use el in new code
15897      * @type {Roo.Layout}
15898      */
15899     panel : false,
15900     
15901     /**
15902      * @property layout
15903      * for BC  - use el in new code
15904      * @type {Roo.Layout}
15905      */
15906     layout : false,
15907     
15908      /**
15909      * @cfg {Function|boolean} disabled
15910      * If this module is disabled by some rule, return true from the funtion
15911      */
15912     disabled : false,
15913     
15914     /**
15915      * @cfg {String} parent 
15916      * Name of parent element which it get xtype added to..
15917      */
15918     parent: false,
15919     
15920     /**
15921      * @cfg {String} order
15922      * Used to set the order in which elements are created (usefull for multiple tabs)
15923      */
15924     
15925     order : false,
15926     /**
15927      * @cfg {String} name
15928      * String to display while loading.
15929      */
15930     name : false,
15931     /**
15932      * @cfg {String} region
15933      * Region to render component to (defaults to center)
15934      */
15935     region : 'center',
15936     
15937     /**
15938      * @cfg {Array} items
15939      * A single item array - the first element is the root of the tree..
15940      * It's done this way to stay compatible with the Xtype system...
15941      */
15942     items : false,
15943     
15944     /**
15945      * @property _tree
15946      * The method that retuns the tree of parts that make up this compoennt 
15947      * @type {function}
15948      */
15949     _tree  : false,
15950     
15951      /**
15952      * render
15953      * render element to dom or tree
15954      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15955      */
15956     
15957     render : function(el)
15958     {
15959         
15960         el = el || false;
15961         var hp = this.parent ? 1 : 0;
15962         Roo.debug &&  Roo.log(this);
15963         
15964         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15965             // if parent is a '#.....' string, then let's use that..
15966             var ename = this.parent.substr(1);
15967             this.parent = false;
15968             Roo.debug && Roo.log(ename);
15969             switch (ename) {
15970                 case 'bootstrap-body' :
15971                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15972                         this.parent = { el :  new  Roo.bootstrap.Body() };
15973                         Roo.debug && Roo.log("setting el to doc body");
15974                          
15975                     } else {
15976                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15977                     }
15978                     break;
15979                 case 'bootstrap':
15980                     this.parent = { el : true};
15981                     // fall through
15982                 default:
15983                     el = Roo.get(ename);
15984                     break;
15985             }
15986                 
15987             
15988             if (!el && !this.parent) {
15989                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15990                 return;
15991             }
15992         }
15993         Roo.debug && Roo.log("EL:");
15994         Roo.debug && Roo.log(el);
15995         Roo.debug && Roo.log("this.parent.el:");
15996         Roo.debug && Roo.log(this.parent.el);
15997         
15998         var tree = this._tree ? this._tree() : this.tree();
15999
16000         // altertive root elements ??? - we need a better way to indicate these.
16001         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16002                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16003         
16004         if (!this.parent && is_alt) {
16005             //el = Roo.get(document.body);
16006             this.parent = { el : true };
16007         }
16008             
16009             
16010         
16011         if (!this.parent) {
16012             
16013             Roo.debug && Roo.log("no parent - creating one");
16014             
16015             el = el ? Roo.get(el) : false;      
16016             
16017             // it's a top level one..
16018             this.parent =  {
16019                 el : new Roo.BorderLayout(el || document.body, {
16020                 
16021                      center: {
16022                          titlebar: false,
16023                          autoScroll:false,
16024                          closeOnTab: true,
16025                          tabPosition: 'top',
16026                           //resizeTabs: true,
16027                          alwaysShowTabs: el && hp? false :  true,
16028                          hideTabs: el || !hp ? true :  false,
16029                          minTabWidth: 140
16030                      }
16031                  })
16032             }
16033         }
16034         
16035         if (!this.parent.el) {
16036                 // probably an old style ctor, which has been disabled.
16037                 return;
16038
16039         }
16040                 // The 'tree' method is  '_tree now' 
16041             
16042         tree.region = tree.region || this.region;
16043         
16044         if (this.parent.el === true) {
16045             // bootstrap... - body..
16046             this.parent.el = Roo.factory(tree);
16047         }
16048         
16049         this.el = this.parent.el.addxtype(tree);
16050         this.fireEvent('built', this);
16051         
16052         this.panel = this.el;
16053         this.layout = this.panel.layout;
16054         this.parentLayout = this.parent.layout  || false;  
16055          
16056     }
16057     
16058 });
16059
16060 Roo.apply(Roo.XComponent, {
16061     /**
16062      * @property  hideProgress
16063      * true to disable the building progress bar.. usefull on single page renders.
16064      * @type Boolean
16065      */
16066     hideProgress : false,
16067     /**
16068      * @property  buildCompleted
16069      * True when the builder has completed building the interface.
16070      * @type Boolean
16071      */
16072     buildCompleted : false,
16073      
16074     /**
16075      * @property  topModule
16076      * the upper most module - uses document.element as it's constructor.
16077      * @type Object
16078      */
16079      
16080     topModule  : false,
16081       
16082     /**
16083      * @property  modules
16084      * array of modules to be created by registration system.
16085      * @type {Array} of Roo.XComponent
16086      */
16087     
16088     modules : [],
16089     /**
16090      * @property  elmodules
16091      * array of modules to be created by which use #ID 
16092      * @type {Array} of Roo.XComponent
16093      */
16094      
16095     elmodules : [],
16096
16097      /**
16098      * @property  build_from_html
16099      * Build elements from html - used by bootstrap HTML stuff 
16100      *    - this is cleared after build is completed
16101      * @type {boolean} true  (default false)
16102      */
16103      
16104     build_from_html : false,
16105
16106     /**
16107      * Register components to be built later.
16108      *
16109      * This solves the following issues
16110      * - Building is not done on page load, but after an authentication process has occured.
16111      * - Interface elements are registered on page load
16112      * - Parent Interface elements may not be loaded before child, so this handles that..
16113      * 
16114      *
16115      * example:
16116      * 
16117      * MyApp.register({
16118           order : '000001',
16119           module : 'Pman.Tab.projectMgr',
16120           region : 'center',
16121           parent : 'Pman.layout',
16122           disabled : false,  // or use a function..
16123         })
16124      
16125      * * @param {Object} details about module
16126      */
16127     register : function(obj) {
16128                 
16129         Roo.XComponent.event.fireEvent('register', obj);
16130         switch(typeof(obj.disabled) ) {
16131                 
16132             case 'undefined':
16133                 break;
16134             
16135             case 'function':
16136                 if ( obj.disabled() ) {
16137                         return;
16138                 }
16139                 break;
16140             
16141             default:
16142                 if (obj.disabled) {
16143                         return;
16144                 }
16145                 break;
16146         }
16147                 
16148         this.modules.push(obj);
16149          
16150     },
16151     /**
16152      * convert a string to an object..
16153      * eg. 'AAA.BBB' -> finds AAA.BBB
16154
16155      */
16156     
16157     toObject : function(str)
16158     {
16159         if (!str || typeof(str) == 'object') {
16160             return str;
16161         }
16162         if (str.substring(0,1) == '#') {
16163             return str;
16164         }
16165
16166         var ar = str.split('.');
16167         var rt, o;
16168         rt = ar.shift();
16169             /** eval:var:o */
16170         try {
16171             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16172         } catch (e) {
16173             throw "Module not found : " + str;
16174         }
16175         
16176         if (o === false) {
16177             throw "Module not found : " + str;
16178         }
16179         Roo.each(ar, function(e) {
16180             if (typeof(o[e]) == 'undefined') {
16181                 throw "Module not found : " + str;
16182             }
16183             o = o[e];
16184         });
16185         
16186         return o;
16187         
16188     },
16189     
16190     
16191     /**
16192      * move modules into their correct place in the tree..
16193      * 
16194      */
16195     preBuild : function ()
16196     {
16197         var _t = this;
16198         Roo.each(this.modules , function (obj)
16199         {
16200             Roo.XComponent.event.fireEvent('beforebuild', obj);
16201             
16202             var opar = obj.parent;
16203             try { 
16204                 obj.parent = this.toObject(opar);
16205             } catch(e) {
16206                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16207                 return;
16208             }
16209             
16210             if (!obj.parent) {
16211                 Roo.debug && Roo.log("GOT top level module");
16212                 Roo.debug && Roo.log(obj);
16213                 obj.modules = new Roo.util.MixedCollection(false, 
16214                     function(o) { return o.order + '' }
16215                 );
16216                 this.topModule = obj;
16217                 return;
16218             }
16219                         // parent is a string (usually a dom element name..)
16220             if (typeof(obj.parent) == 'string') {
16221                 this.elmodules.push(obj);
16222                 return;
16223             }
16224             if (obj.parent.constructor != Roo.XComponent) {
16225                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16226             }
16227             if (!obj.parent.modules) {
16228                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16229                     function(o) { return o.order + '' }
16230                 );
16231             }
16232             if (obj.parent.disabled) {
16233                 obj.disabled = true;
16234             }
16235             obj.parent.modules.add(obj);
16236         }, this);
16237     },
16238     
16239      /**
16240      * make a list of modules to build.
16241      * @return {Array} list of modules. 
16242      */ 
16243     
16244     buildOrder : function()
16245     {
16246         var _this = this;
16247         var cmp = function(a,b) {   
16248             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16249         };
16250         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16251             throw "No top level modules to build";
16252         }
16253         
16254         // make a flat list in order of modules to build.
16255         var mods = this.topModule ? [ this.topModule ] : [];
16256                 
16257         
16258         // elmodules (is a list of DOM based modules )
16259         Roo.each(this.elmodules, function(e) {
16260             mods.push(e);
16261             if (!this.topModule &&
16262                 typeof(e.parent) == 'string' &&
16263                 e.parent.substring(0,1) == '#' &&
16264                 Roo.get(e.parent.substr(1))
16265                ) {
16266                 
16267                 _this.topModule = e;
16268             }
16269             
16270         });
16271
16272         
16273         // add modules to their parents..
16274         var addMod = function(m) {
16275             Roo.debug && Roo.log("build Order: add: " + m.name);
16276                 
16277             mods.push(m);
16278             if (m.modules && !m.disabled) {
16279                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16280                 m.modules.keySort('ASC',  cmp );
16281                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16282     
16283                 m.modules.each(addMod);
16284             } else {
16285                 Roo.debug && Roo.log("build Order: no child modules");
16286             }
16287             // not sure if this is used any more..
16288             if (m.finalize) {
16289                 m.finalize.name = m.name + " (clean up) ";
16290                 mods.push(m.finalize);
16291             }
16292             
16293         }
16294         if (this.topModule && this.topModule.modules) { 
16295             this.topModule.modules.keySort('ASC',  cmp );
16296             this.topModule.modules.each(addMod);
16297         } 
16298         return mods;
16299     },
16300     
16301      /**
16302      * Build the registered modules.
16303      * @param {Object} parent element.
16304      * @param {Function} optional method to call after module has been added.
16305      * 
16306      */ 
16307    
16308     build : function(opts) 
16309     {
16310         
16311         if (typeof(opts) != 'undefined') {
16312             Roo.apply(this,opts);
16313         }
16314         
16315         this.preBuild();
16316         var mods = this.buildOrder();
16317       
16318         //this.allmods = mods;
16319         //Roo.debug && Roo.log(mods);
16320         //return;
16321         if (!mods.length) { // should not happen
16322             throw "NO modules!!!";
16323         }
16324         
16325         
16326         var msg = "Building Interface...";
16327         // flash it up as modal - so we store the mask!?
16328         if (!this.hideProgress && Roo.MessageBox) {
16329             Roo.MessageBox.show({ title: 'loading' });
16330             Roo.MessageBox.show({
16331                title: "Please wait...",
16332                msg: msg,
16333                width:450,
16334                progress:true,
16335                closable:false,
16336                modal: false
16337               
16338             });
16339         }
16340         var total = mods.length;
16341         
16342         var _this = this;
16343         var progressRun = function() {
16344             if (!mods.length) {
16345                 Roo.debug && Roo.log('hide?');
16346                 if (!this.hideProgress && Roo.MessageBox) {
16347                     Roo.MessageBox.hide();
16348                 }
16349                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16350                 
16351                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16352                 
16353                 // THE END...
16354                 return false;   
16355             }
16356             
16357             var m = mods.shift();
16358             
16359             
16360             Roo.debug && Roo.log(m);
16361             // not sure if this is supported any more.. - modules that are are just function
16362             if (typeof(m) == 'function') { 
16363                 m.call(this);
16364                 return progressRun.defer(10, _this);
16365             } 
16366             
16367             
16368             msg = "Building Interface " + (total  - mods.length) + 
16369                     " of " + total + 
16370                     (m.name ? (' - ' + m.name) : '');
16371                         Roo.debug && Roo.log(msg);
16372             if (!this.hideProgress &&  Roo.MessageBox) { 
16373                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16374             }
16375             
16376          
16377             // is the module disabled?
16378             var disabled = (typeof(m.disabled) == 'function') ?
16379                 m.disabled.call(m.module.disabled) : m.disabled;    
16380             
16381             
16382             if (disabled) {
16383                 return progressRun(); // we do not update the display!
16384             }
16385             
16386             // now build 
16387             
16388                         
16389                         
16390             m.render();
16391             // it's 10 on top level, and 1 on others??? why...
16392             return progressRun.defer(10, _this);
16393              
16394         }
16395         progressRun.defer(1, _this);
16396      
16397         
16398         
16399     },
16400         
16401         
16402         /**
16403          * Event Object.
16404          *
16405          *
16406          */
16407         event: false, 
16408     /**
16409          * wrapper for event.on - aliased later..  
16410          * Typically use to register a event handler for register:
16411          *
16412          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16413          *
16414          */
16415     on : false
16416    
16417     
16418     
16419 });
16420
16421 Roo.XComponent.event = new Roo.util.Observable({
16422                 events : { 
16423                         /**
16424                          * @event register
16425                          * Fires when an Component is registered,
16426                          * set the disable property on the Component to stop registration.
16427                          * @param {Roo.XComponent} c the component being registerd.
16428                          * 
16429                          */
16430                         'register' : true,
16431             /**
16432                          * @event beforebuild
16433                          * Fires before each Component is built
16434                          * can be used to apply permissions.
16435                          * @param {Roo.XComponent} c the component being registerd.
16436                          * 
16437                          */
16438                         'beforebuild' : true,
16439                         /**
16440                          * @event buildcomplete
16441                          * Fires on the top level element when all elements have been built
16442                          * @param {Roo.XComponent} the top level component.
16443                          */
16444                         'buildcomplete' : true
16445                         
16446                 }
16447 });
16448
16449 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16450  /*
16451  * Based on:
16452  * Ext JS Library 1.1.1
16453  * Copyright(c) 2006-2007, Ext JS, LLC.
16454  *
16455  * Originally Released Under LGPL - original licence link has changed is not relivant.
16456  *
16457  * Fork - LGPL
16458  * <script type="text/javascript">
16459  */
16460
16461
16462
16463 /*
16464  * These classes are derivatives of the similarly named classes in the YUI Library.
16465  * The original license:
16466  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16467  * Code licensed under the BSD License:
16468  * http://developer.yahoo.net/yui/license.txt
16469  */
16470
16471 (function() {
16472
16473 var Event=Roo.EventManager;
16474 var Dom=Roo.lib.Dom;
16475
16476 /**
16477  * @class Roo.dd.DragDrop
16478  * @extends Roo.util.Observable
16479  * Defines the interface and base operation of items that that can be
16480  * dragged or can be drop targets.  It was designed to be extended, overriding
16481  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16482  * Up to three html elements can be associated with a DragDrop instance:
16483  * <ul>
16484  * <li>linked element: the element that is passed into the constructor.
16485  * This is the element which defines the boundaries for interaction with
16486  * other DragDrop objects.</li>
16487  * <li>handle element(s): The drag operation only occurs if the element that
16488  * was clicked matches a handle element.  By default this is the linked
16489  * element, but there are times that you will want only a portion of the
16490  * linked element to initiate the drag operation, and the setHandleElId()
16491  * method provides a way to define this.</li>
16492  * <li>drag element: this represents the element that would be moved along
16493  * with the cursor during a drag operation.  By default, this is the linked
16494  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16495  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16496  * </li>
16497  * </ul>
16498  * This class should not be instantiated until the onload event to ensure that
16499  * the associated elements are available.
16500  * The following would define a DragDrop obj that would interact with any
16501  * other DragDrop obj in the "group1" group:
16502  * <pre>
16503  *  dd = new Roo.dd.DragDrop("div1", "group1");
16504  * </pre>
16505  * Since none of the event handlers have been implemented, nothing would
16506  * actually happen if you were to run the code above.  Normally you would
16507  * override this class or one of the default implementations, but you can
16508  * also override the methods you want on an instance of the class...
16509  * <pre>
16510  *  dd.onDragDrop = function(e, id) {
16511  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16512  *  }
16513  * </pre>
16514  * @constructor
16515  * @param {String} id of the element that is linked to this instance
16516  * @param {String} sGroup the group of related DragDrop objects
16517  * @param {object} config an object containing configurable attributes
16518  *                Valid properties for DragDrop:
16519  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16520  */
16521 Roo.dd.DragDrop = function(id, sGroup, config) {
16522     if (id) {
16523         this.init(id, sGroup, config);
16524     }
16525     
16526 };
16527
16528 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16529
16530     /**
16531      * The id of the element associated with this object.  This is what we
16532      * refer to as the "linked element" because the size and position of
16533      * this element is used to determine when the drag and drop objects have
16534      * interacted.
16535      * @property id
16536      * @type String
16537      */
16538     id: null,
16539
16540     /**
16541      * Configuration attributes passed into the constructor
16542      * @property config
16543      * @type object
16544      */
16545     config: null,
16546
16547     /**
16548      * The id of the element that will be dragged.  By default this is same
16549      * as the linked element , but could be changed to another element. Ex:
16550      * Roo.dd.DDProxy
16551      * @property dragElId
16552      * @type String
16553      * @private
16554      */
16555     dragElId: null,
16556
16557     /**
16558      * the id of the element that initiates the drag operation.  By default
16559      * this is the linked element, but could be changed to be a child of this
16560      * element.  This lets us do things like only starting the drag when the
16561      * header element within the linked html element is clicked.
16562      * @property handleElId
16563      * @type String
16564      * @private
16565      */
16566     handleElId: null,
16567
16568     /**
16569      * An associative array of HTML tags that will be ignored if clicked.
16570      * @property invalidHandleTypes
16571      * @type {string: string}
16572      */
16573     invalidHandleTypes: null,
16574
16575     /**
16576      * An associative array of ids for elements that will be ignored if clicked
16577      * @property invalidHandleIds
16578      * @type {string: string}
16579      */
16580     invalidHandleIds: null,
16581
16582     /**
16583      * An indexted array of css class names for elements that will be ignored
16584      * if clicked.
16585      * @property invalidHandleClasses
16586      * @type string[]
16587      */
16588     invalidHandleClasses: null,
16589
16590     /**
16591      * The linked element's absolute X position at the time the drag was
16592      * started
16593      * @property startPageX
16594      * @type int
16595      * @private
16596      */
16597     startPageX: 0,
16598
16599     /**
16600      * The linked element's absolute X position at the time the drag was
16601      * started
16602      * @property startPageY
16603      * @type int
16604      * @private
16605      */
16606     startPageY: 0,
16607
16608     /**
16609      * The group defines a logical collection of DragDrop objects that are
16610      * related.  Instances only get events when interacting with other
16611      * DragDrop object in the same group.  This lets us define multiple
16612      * groups using a single DragDrop subclass if we want.
16613      * @property groups
16614      * @type {string: string}
16615      */
16616     groups: null,
16617
16618     /**
16619      * Individual drag/drop instances can be locked.  This will prevent
16620      * onmousedown start drag.
16621      * @property locked
16622      * @type boolean
16623      * @private
16624      */
16625     locked: false,
16626
16627     /**
16628      * Lock this instance
16629      * @method lock
16630      */
16631     lock: function() { this.locked = true; },
16632
16633     /**
16634      * Unlock this instace
16635      * @method unlock
16636      */
16637     unlock: function() { this.locked = false; },
16638
16639     /**
16640      * By default, all insances can be a drop target.  This can be disabled by
16641      * setting isTarget to false.
16642      * @method isTarget
16643      * @type boolean
16644      */
16645     isTarget: true,
16646
16647     /**
16648      * The padding configured for this drag and drop object for calculating
16649      * the drop zone intersection with this object.
16650      * @method padding
16651      * @type int[]
16652      */
16653     padding: null,
16654
16655     /**
16656      * Cached reference to the linked element
16657      * @property _domRef
16658      * @private
16659      */
16660     _domRef: null,
16661
16662     /**
16663      * Internal typeof flag
16664      * @property __ygDragDrop
16665      * @private
16666      */
16667     __ygDragDrop: true,
16668
16669     /**
16670      * Set to true when horizontal contraints are applied
16671      * @property constrainX
16672      * @type boolean
16673      * @private
16674      */
16675     constrainX: false,
16676
16677     /**
16678      * Set to true when vertical contraints are applied
16679      * @property constrainY
16680      * @type boolean
16681      * @private
16682      */
16683     constrainY: false,
16684
16685     /**
16686      * The left constraint
16687      * @property minX
16688      * @type int
16689      * @private
16690      */
16691     minX: 0,
16692
16693     /**
16694      * The right constraint
16695      * @property maxX
16696      * @type int
16697      * @private
16698      */
16699     maxX: 0,
16700
16701     /**
16702      * The up constraint
16703      * @property minY
16704      * @type int
16705      * @type int
16706      * @private
16707      */
16708     minY: 0,
16709
16710     /**
16711      * The down constraint
16712      * @property maxY
16713      * @type int
16714      * @private
16715      */
16716     maxY: 0,
16717
16718     /**
16719      * Maintain offsets when we resetconstraints.  Set to true when you want
16720      * the position of the element relative to its parent to stay the same
16721      * when the page changes
16722      *
16723      * @property maintainOffset
16724      * @type boolean
16725      */
16726     maintainOffset: false,
16727
16728     /**
16729      * Array of pixel locations the element will snap to if we specified a
16730      * horizontal graduation/interval.  This array is generated automatically
16731      * when you define a tick interval.
16732      * @property xTicks
16733      * @type int[]
16734      */
16735     xTicks: null,
16736
16737     /**
16738      * Array of pixel locations the element will snap to if we specified a
16739      * vertical graduation/interval.  This array is generated automatically
16740      * when you define a tick interval.
16741      * @property yTicks
16742      * @type int[]
16743      */
16744     yTicks: null,
16745
16746     /**
16747      * By default the drag and drop instance will only respond to the primary
16748      * button click (left button for a right-handed mouse).  Set to true to
16749      * allow drag and drop to start with any mouse click that is propogated
16750      * by the browser
16751      * @property primaryButtonOnly
16752      * @type boolean
16753      */
16754     primaryButtonOnly: true,
16755
16756     /**
16757      * The availabe property is false until the linked dom element is accessible.
16758      * @property available
16759      * @type boolean
16760      */
16761     available: false,
16762
16763     /**
16764      * By default, drags can only be initiated if the mousedown occurs in the
16765      * region the linked element is.  This is done in part to work around a
16766      * bug in some browsers that mis-report the mousedown if the previous
16767      * mouseup happened outside of the window.  This property is set to true
16768      * if outer handles are defined.
16769      *
16770      * @property hasOuterHandles
16771      * @type boolean
16772      * @default false
16773      */
16774     hasOuterHandles: false,
16775
16776     /**
16777      * Code that executes immediately before the startDrag event
16778      * @method b4StartDrag
16779      * @private
16780      */
16781     b4StartDrag: function(x, y) { },
16782
16783     /**
16784      * Abstract method called after a drag/drop object is clicked
16785      * and the drag or mousedown time thresholds have beeen met.
16786      * @method startDrag
16787      * @param {int} X click location
16788      * @param {int} Y click location
16789      */
16790     startDrag: function(x, y) { /* override this */ },
16791
16792     /**
16793      * Code that executes immediately before the onDrag event
16794      * @method b4Drag
16795      * @private
16796      */
16797     b4Drag: function(e) { },
16798
16799     /**
16800      * Abstract method called during the onMouseMove event while dragging an
16801      * object.
16802      * @method onDrag
16803      * @param {Event} e the mousemove event
16804      */
16805     onDrag: function(e) { /* override this */ },
16806
16807     /**
16808      * Abstract method called when this element fist begins hovering over
16809      * another DragDrop obj
16810      * @method onDragEnter
16811      * @param {Event} e the mousemove event
16812      * @param {String|DragDrop[]} id In POINT mode, the element
16813      * id this is hovering over.  In INTERSECT mode, an array of one or more
16814      * dragdrop items being hovered over.
16815      */
16816     onDragEnter: function(e, id) { /* override this */ },
16817
16818     /**
16819      * Code that executes immediately before the onDragOver event
16820      * @method b4DragOver
16821      * @private
16822      */
16823     b4DragOver: function(e) { },
16824
16825     /**
16826      * Abstract method called when this element is hovering over another
16827      * DragDrop obj
16828      * @method onDragOver
16829      * @param {Event} e the mousemove event
16830      * @param {String|DragDrop[]} id In POINT mode, the element
16831      * id this is hovering over.  In INTERSECT mode, an array of dd items
16832      * being hovered over.
16833      */
16834     onDragOver: function(e, id) { /* override this */ },
16835
16836     /**
16837      * Code that executes immediately before the onDragOut event
16838      * @method b4DragOut
16839      * @private
16840      */
16841     b4DragOut: function(e) { },
16842
16843     /**
16844      * Abstract method called when we are no longer hovering over an element
16845      * @method onDragOut
16846      * @param {Event} e the mousemove event
16847      * @param {String|DragDrop[]} id In POINT mode, the element
16848      * id this was hovering over.  In INTERSECT mode, an array of dd items
16849      * that the mouse is no longer over.
16850      */
16851     onDragOut: function(e, id) { /* override this */ },
16852
16853     /**
16854      * Code that executes immediately before the onDragDrop event
16855      * @method b4DragDrop
16856      * @private
16857      */
16858     b4DragDrop: function(e) { },
16859
16860     /**
16861      * Abstract method called when this item is dropped on another DragDrop
16862      * obj
16863      * @method onDragDrop
16864      * @param {Event} e the mouseup event
16865      * @param {String|DragDrop[]} id In POINT mode, the element
16866      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16867      * was dropped on.
16868      */
16869     onDragDrop: function(e, id) { /* override this */ },
16870
16871     /**
16872      * Abstract method called when this item is dropped on an area with no
16873      * drop target
16874      * @method onInvalidDrop
16875      * @param {Event} e the mouseup event
16876      */
16877     onInvalidDrop: function(e) { /* override this */ },
16878
16879     /**
16880      * Code that executes immediately before the endDrag event
16881      * @method b4EndDrag
16882      * @private
16883      */
16884     b4EndDrag: function(e) { },
16885
16886     /**
16887      * Fired when we are done dragging the object
16888      * @method endDrag
16889      * @param {Event} e the mouseup event
16890      */
16891     endDrag: function(e) { /* override this */ },
16892
16893     /**
16894      * Code executed immediately before the onMouseDown event
16895      * @method b4MouseDown
16896      * @param {Event} e the mousedown event
16897      * @private
16898      */
16899     b4MouseDown: function(e) {  },
16900
16901     /**
16902      * Event handler that fires when a drag/drop obj gets a mousedown
16903      * @method onMouseDown
16904      * @param {Event} e the mousedown event
16905      */
16906     onMouseDown: function(e) { /* override this */ },
16907
16908     /**
16909      * Event handler that fires when a drag/drop obj gets a mouseup
16910      * @method onMouseUp
16911      * @param {Event} e the mouseup event
16912      */
16913     onMouseUp: function(e) { /* override this */ },
16914
16915     /**
16916      * Override the onAvailable method to do what is needed after the initial
16917      * position was determined.
16918      * @method onAvailable
16919      */
16920     onAvailable: function () {
16921     },
16922
16923     /*
16924      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16925      * @type Object
16926      */
16927     defaultPadding : {left:0, right:0, top:0, bottom:0},
16928
16929     /*
16930      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16931  *
16932  * Usage:
16933  <pre><code>
16934  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16935                 { dragElId: "existingProxyDiv" });
16936  dd.startDrag = function(){
16937      this.constrainTo("parent-id");
16938  };
16939  </code></pre>
16940  * Or you can initalize it using the {@link Roo.Element} object:
16941  <pre><code>
16942  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16943      startDrag : function(){
16944          this.constrainTo("parent-id");
16945      }
16946  });
16947  </code></pre>
16948      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16949      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16950      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16951      * an object containing the sides to pad. For example: {right:10, bottom:10}
16952      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16953      */
16954     constrainTo : function(constrainTo, pad, inContent){
16955         if(typeof pad == "number"){
16956             pad = {left: pad, right:pad, top:pad, bottom:pad};
16957         }
16958         pad = pad || this.defaultPadding;
16959         var b = Roo.get(this.getEl()).getBox();
16960         var ce = Roo.get(constrainTo);
16961         var s = ce.getScroll();
16962         var c, cd = ce.dom;
16963         if(cd == document.body){
16964             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16965         }else{
16966             xy = ce.getXY();
16967             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16968         }
16969
16970
16971         var topSpace = b.y - c.y;
16972         var leftSpace = b.x - c.x;
16973
16974         this.resetConstraints();
16975         this.setXConstraint(leftSpace - (pad.left||0), // left
16976                 c.width - leftSpace - b.width - (pad.right||0) //right
16977         );
16978         this.setYConstraint(topSpace - (pad.top||0), //top
16979                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16980         );
16981     },
16982
16983     /**
16984      * Returns a reference to the linked element
16985      * @method getEl
16986      * @return {HTMLElement} the html element
16987      */
16988     getEl: function() {
16989         if (!this._domRef) {
16990             this._domRef = Roo.getDom(this.id);
16991         }
16992
16993         return this._domRef;
16994     },
16995
16996     /**
16997      * Returns a reference to the actual element to drag.  By default this is
16998      * the same as the html element, but it can be assigned to another
16999      * element. An example of this can be found in Roo.dd.DDProxy
17000      * @method getDragEl
17001      * @return {HTMLElement} the html element
17002      */
17003     getDragEl: function() {
17004         return Roo.getDom(this.dragElId);
17005     },
17006
17007     /**
17008      * Sets up the DragDrop object.  Must be called in the constructor of any
17009      * Roo.dd.DragDrop subclass
17010      * @method init
17011      * @param id the id of the linked element
17012      * @param {String} sGroup the group of related items
17013      * @param {object} config configuration attributes
17014      */
17015     init: function(id, sGroup, config) {
17016         this.initTarget(id, sGroup, config);
17017         if (!Roo.isTouch) {
17018             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17019         }
17020         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17021         // Event.on(this.id, "selectstart", Event.preventDefault);
17022     },
17023
17024     /**
17025      * Initializes Targeting functionality only... the object does not
17026      * get a mousedown handler.
17027      * @method initTarget
17028      * @param id the id of the linked element
17029      * @param {String} sGroup the group of related items
17030      * @param {object} config configuration attributes
17031      */
17032     initTarget: function(id, sGroup, config) {
17033
17034         // configuration attributes
17035         this.config = config || {};
17036
17037         // create a local reference to the drag and drop manager
17038         this.DDM = Roo.dd.DDM;
17039         // initialize the groups array
17040         this.groups = {};
17041
17042         // assume that we have an element reference instead of an id if the
17043         // parameter is not a string
17044         if (typeof id !== "string") {
17045             id = Roo.id(id);
17046         }
17047
17048         // set the id
17049         this.id = id;
17050
17051         // add to an interaction group
17052         this.addToGroup((sGroup) ? sGroup : "default");
17053
17054         // We don't want to register this as the handle with the manager
17055         // so we just set the id rather than calling the setter.
17056         this.handleElId = id;
17057
17058         // the linked element is the element that gets dragged by default
17059         this.setDragElId(id);
17060
17061         // by default, clicked anchors will not start drag operations.
17062         this.invalidHandleTypes = { A: "A" };
17063         this.invalidHandleIds = {};
17064         this.invalidHandleClasses = [];
17065
17066         this.applyConfig();
17067
17068         this.handleOnAvailable();
17069     },
17070
17071     /**
17072      * Applies the configuration parameters that were passed into the constructor.
17073      * This is supposed to happen at each level through the inheritance chain.  So
17074      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17075      * DragDrop in order to get all of the parameters that are available in
17076      * each object.
17077      * @method applyConfig
17078      */
17079     applyConfig: function() {
17080
17081         // configurable properties:
17082         //    padding, isTarget, maintainOffset, primaryButtonOnly
17083         this.padding           = this.config.padding || [0, 0, 0, 0];
17084         this.isTarget          = (this.config.isTarget !== false);
17085         this.maintainOffset    = (this.config.maintainOffset);
17086         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17087
17088     },
17089
17090     /**
17091      * Executed when the linked element is available
17092      * @method handleOnAvailable
17093      * @private
17094      */
17095     handleOnAvailable: function() {
17096         this.available = true;
17097         this.resetConstraints();
17098         this.onAvailable();
17099     },
17100
17101      /**
17102      * Configures the padding for the target zone in px.  Effectively expands
17103      * (or reduces) the virtual object size for targeting calculations.
17104      * Supports css-style shorthand; if only one parameter is passed, all sides
17105      * will have that padding, and if only two are passed, the top and bottom
17106      * will have the first param, the left and right the second.
17107      * @method setPadding
17108      * @param {int} iTop    Top pad
17109      * @param {int} iRight  Right pad
17110      * @param {int} iBot    Bot pad
17111      * @param {int} iLeft   Left pad
17112      */
17113     setPadding: function(iTop, iRight, iBot, iLeft) {
17114         // this.padding = [iLeft, iRight, iTop, iBot];
17115         if (!iRight && 0 !== iRight) {
17116             this.padding = [iTop, iTop, iTop, iTop];
17117         } else if (!iBot && 0 !== iBot) {
17118             this.padding = [iTop, iRight, iTop, iRight];
17119         } else {
17120             this.padding = [iTop, iRight, iBot, iLeft];
17121         }
17122     },
17123
17124     /**
17125      * Stores the initial placement of the linked element.
17126      * @method setInitialPosition
17127      * @param {int} diffX   the X offset, default 0
17128      * @param {int} diffY   the Y offset, default 0
17129      */
17130     setInitPosition: function(diffX, diffY) {
17131         var el = this.getEl();
17132
17133         if (!this.DDM.verifyEl(el)) {
17134             return;
17135         }
17136
17137         var dx = diffX || 0;
17138         var dy = diffY || 0;
17139
17140         var p = Dom.getXY( el );
17141
17142         this.initPageX = p[0] - dx;
17143         this.initPageY = p[1] - dy;
17144
17145         this.lastPageX = p[0];
17146         this.lastPageY = p[1];
17147
17148
17149         this.setStartPosition(p);
17150     },
17151
17152     /**
17153      * Sets the start position of the element.  This is set when the obj
17154      * is initialized, the reset when a drag is started.
17155      * @method setStartPosition
17156      * @param pos current position (from previous lookup)
17157      * @private
17158      */
17159     setStartPosition: function(pos) {
17160         var p = pos || Dom.getXY( this.getEl() );
17161         this.deltaSetXY = null;
17162
17163         this.startPageX = p[0];
17164         this.startPageY = p[1];
17165     },
17166
17167     /**
17168      * Add this instance to a group of related drag/drop objects.  All
17169      * instances belong to at least one group, and can belong to as many
17170      * groups as needed.
17171      * @method addToGroup
17172      * @param sGroup {string} the name of the group
17173      */
17174     addToGroup: function(sGroup) {
17175         this.groups[sGroup] = true;
17176         this.DDM.regDragDrop(this, sGroup);
17177     },
17178
17179     /**
17180      * Remove's this instance from the supplied interaction group
17181      * @method removeFromGroup
17182      * @param {string}  sGroup  The group to drop
17183      */
17184     removeFromGroup: function(sGroup) {
17185         if (this.groups[sGroup]) {
17186             delete this.groups[sGroup];
17187         }
17188
17189         this.DDM.removeDDFromGroup(this, sGroup);
17190     },
17191
17192     /**
17193      * Allows you to specify that an element other than the linked element
17194      * will be moved with the cursor during a drag
17195      * @method setDragElId
17196      * @param id {string} the id of the element that will be used to initiate the drag
17197      */
17198     setDragElId: function(id) {
17199         this.dragElId = id;
17200     },
17201
17202     /**
17203      * Allows you to specify a child of the linked element that should be
17204      * used to initiate the drag operation.  An example of this would be if
17205      * you have a content div with text and links.  Clicking anywhere in the
17206      * content area would normally start the drag operation.  Use this method
17207      * to specify that an element inside of the content div is the element
17208      * that starts the drag operation.
17209      * @method setHandleElId
17210      * @param id {string} the id of the element that will be used to
17211      * initiate the drag.
17212      */
17213     setHandleElId: function(id) {
17214         if (typeof id !== "string") {
17215             id = Roo.id(id);
17216         }
17217         this.handleElId = id;
17218         this.DDM.regHandle(this.id, id);
17219     },
17220
17221     /**
17222      * Allows you to set an element outside of the linked element as a drag
17223      * handle
17224      * @method setOuterHandleElId
17225      * @param id the id of the element that will be used to initiate the drag
17226      */
17227     setOuterHandleElId: function(id) {
17228         if (typeof id !== "string") {
17229             id = Roo.id(id);
17230         }
17231         Event.on(id, "mousedown",
17232                 this.handleMouseDown, this);
17233         this.setHandleElId(id);
17234
17235         this.hasOuterHandles = true;
17236     },
17237
17238     /**
17239      * Remove all drag and drop hooks for this element
17240      * @method unreg
17241      */
17242     unreg: function() {
17243         Event.un(this.id, "mousedown",
17244                 this.handleMouseDown);
17245         Event.un(this.id, "touchstart",
17246                 this.handleMouseDown);
17247         this._domRef = null;
17248         this.DDM._remove(this);
17249     },
17250
17251     destroy : function(){
17252         this.unreg();
17253     },
17254
17255     /**
17256      * Returns true if this instance is locked, or the drag drop mgr is locked
17257      * (meaning that all drag/drop is disabled on the page.)
17258      * @method isLocked
17259      * @return {boolean} true if this obj or all drag/drop is locked, else
17260      * false
17261      */
17262     isLocked: function() {
17263         return (this.DDM.isLocked() || this.locked);
17264     },
17265
17266     /**
17267      * Fired when this object is clicked
17268      * @method handleMouseDown
17269      * @param {Event} e
17270      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17271      * @private
17272      */
17273     handleMouseDown: function(e, oDD){
17274      
17275         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17276             //Roo.log('not touch/ button !=0');
17277             return;
17278         }
17279         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17280             return; // double touch..
17281         }
17282         
17283
17284         if (this.isLocked()) {
17285             //Roo.log('locked');
17286             return;
17287         }
17288
17289         this.DDM.refreshCache(this.groups);
17290 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17291         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17292         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17293             //Roo.log('no outer handes or not over target');
17294                 // do nothing.
17295         } else {
17296 //            Roo.log('check validator');
17297             if (this.clickValidator(e)) {
17298 //                Roo.log('validate success');
17299                 // set the initial element position
17300                 this.setStartPosition();
17301
17302
17303                 this.b4MouseDown(e);
17304                 this.onMouseDown(e);
17305
17306                 this.DDM.handleMouseDown(e, this);
17307
17308                 this.DDM.stopEvent(e);
17309             } else {
17310
17311
17312             }
17313         }
17314     },
17315
17316     clickValidator: function(e) {
17317         var target = e.getTarget();
17318         return ( this.isValidHandleChild(target) &&
17319                     (this.id == this.handleElId ||
17320                         this.DDM.handleWasClicked(target, this.id)) );
17321     },
17322
17323     /**
17324      * Allows you to specify a tag name that should not start a drag operation
17325      * when clicked.  This is designed to facilitate embedding links within a
17326      * drag handle that do something other than start the drag.
17327      * @method addInvalidHandleType
17328      * @param {string} tagName the type of element to exclude
17329      */
17330     addInvalidHandleType: function(tagName) {
17331         var type = tagName.toUpperCase();
17332         this.invalidHandleTypes[type] = type;
17333     },
17334
17335     /**
17336      * Lets you to specify an element id for a child of a drag handle
17337      * that should not initiate a drag
17338      * @method addInvalidHandleId
17339      * @param {string} id the element id of the element you wish to ignore
17340      */
17341     addInvalidHandleId: function(id) {
17342         if (typeof id !== "string") {
17343             id = Roo.id(id);
17344         }
17345         this.invalidHandleIds[id] = id;
17346     },
17347
17348     /**
17349      * Lets you specify a css class of elements that will not initiate a drag
17350      * @method addInvalidHandleClass
17351      * @param {string} cssClass the class of the elements you wish to ignore
17352      */
17353     addInvalidHandleClass: function(cssClass) {
17354         this.invalidHandleClasses.push(cssClass);
17355     },
17356
17357     /**
17358      * Unsets an excluded tag name set by addInvalidHandleType
17359      * @method removeInvalidHandleType
17360      * @param {string} tagName the type of element to unexclude
17361      */
17362     removeInvalidHandleType: function(tagName) {
17363         var type = tagName.toUpperCase();
17364         // this.invalidHandleTypes[type] = null;
17365         delete this.invalidHandleTypes[type];
17366     },
17367
17368     /**
17369      * Unsets an invalid handle id
17370      * @method removeInvalidHandleId
17371      * @param {string} id the id of the element to re-enable
17372      */
17373     removeInvalidHandleId: function(id) {
17374         if (typeof id !== "string") {
17375             id = Roo.id(id);
17376         }
17377         delete this.invalidHandleIds[id];
17378     },
17379
17380     /**
17381      * Unsets an invalid css class
17382      * @method removeInvalidHandleClass
17383      * @param {string} cssClass the class of the element(s) you wish to
17384      * re-enable
17385      */
17386     removeInvalidHandleClass: function(cssClass) {
17387         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17388             if (this.invalidHandleClasses[i] == cssClass) {
17389                 delete this.invalidHandleClasses[i];
17390             }
17391         }
17392     },
17393
17394     /**
17395      * Checks the tag exclusion list to see if this click should be ignored
17396      * @method isValidHandleChild
17397      * @param {HTMLElement} node the HTMLElement to evaluate
17398      * @return {boolean} true if this is a valid tag type, false if not
17399      */
17400     isValidHandleChild: function(node) {
17401
17402         var valid = true;
17403         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17404         var nodeName;
17405         try {
17406             nodeName = node.nodeName.toUpperCase();
17407         } catch(e) {
17408             nodeName = node.nodeName;
17409         }
17410         valid = valid && !this.invalidHandleTypes[nodeName];
17411         valid = valid && !this.invalidHandleIds[node.id];
17412
17413         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17414             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17415         }
17416
17417
17418         return valid;
17419
17420     },
17421
17422     /**
17423      * Create the array of horizontal tick marks if an interval was specified
17424      * in setXConstraint().
17425      * @method setXTicks
17426      * @private
17427      */
17428     setXTicks: function(iStartX, iTickSize) {
17429         this.xTicks = [];
17430         this.xTickSize = iTickSize;
17431
17432         var tickMap = {};
17433
17434         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17435             if (!tickMap[i]) {
17436                 this.xTicks[this.xTicks.length] = i;
17437                 tickMap[i] = true;
17438             }
17439         }
17440
17441         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17442             if (!tickMap[i]) {
17443                 this.xTicks[this.xTicks.length] = i;
17444                 tickMap[i] = true;
17445             }
17446         }
17447
17448         this.xTicks.sort(this.DDM.numericSort) ;
17449     },
17450
17451     /**
17452      * Create the array of vertical tick marks if an interval was specified in
17453      * setYConstraint().
17454      * @method setYTicks
17455      * @private
17456      */
17457     setYTicks: function(iStartY, iTickSize) {
17458         this.yTicks = [];
17459         this.yTickSize = iTickSize;
17460
17461         var tickMap = {};
17462
17463         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17464             if (!tickMap[i]) {
17465                 this.yTicks[this.yTicks.length] = i;
17466                 tickMap[i] = true;
17467             }
17468         }
17469
17470         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17471             if (!tickMap[i]) {
17472                 this.yTicks[this.yTicks.length] = i;
17473                 tickMap[i] = true;
17474             }
17475         }
17476
17477         this.yTicks.sort(this.DDM.numericSort) ;
17478     },
17479
17480     /**
17481      * By default, the element can be dragged any place on the screen.  Use
17482      * this method to limit the horizontal travel of the element.  Pass in
17483      * 0,0 for the parameters if you want to lock the drag to the y axis.
17484      * @method setXConstraint
17485      * @param {int} iLeft the number of pixels the element can move to the left
17486      * @param {int} iRight the number of pixels the element can move to the
17487      * right
17488      * @param {int} iTickSize optional parameter for specifying that the
17489      * element
17490      * should move iTickSize pixels at a time.
17491      */
17492     setXConstraint: function(iLeft, iRight, iTickSize) {
17493         this.leftConstraint = iLeft;
17494         this.rightConstraint = iRight;
17495
17496         this.minX = this.initPageX - iLeft;
17497         this.maxX = this.initPageX + iRight;
17498         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17499
17500         this.constrainX = true;
17501     },
17502
17503     /**
17504      * Clears any constraints applied to this instance.  Also clears ticks
17505      * since they can't exist independent of a constraint at this time.
17506      * @method clearConstraints
17507      */
17508     clearConstraints: function() {
17509         this.constrainX = false;
17510         this.constrainY = false;
17511         this.clearTicks();
17512     },
17513
17514     /**
17515      * Clears any tick interval defined for this instance
17516      * @method clearTicks
17517      */
17518     clearTicks: function() {
17519         this.xTicks = null;
17520         this.yTicks = null;
17521         this.xTickSize = 0;
17522         this.yTickSize = 0;
17523     },
17524
17525     /**
17526      * By default, the element can be dragged any place on the screen.  Set
17527      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17528      * parameters if you want to lock the drag to the x axis.
17529      * @method setYConstraint
17530      * @param {int} iUp the number of pixels the element can move up
17531      * @param {int} iDown the number of pixels the element can move down
17532      * @param {int} iTickSize optional parameter for specifying that the
17533      * element should move iTickSize pixels at a time.
17534      */
17535     setYConstraint: function(iUp, iDown, iTickSize) {
17536         this.topConstraint = iUp;
17537         this.bottomConstraint = iDown;
17538
17539         this.minY = this.initPageY - iUp;
17540         this.maxY = this.initPageY + iDown;
17541         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17542
17543         this.constrainY = true;
17544
17545     },
17546
17547     /**
17548      * resetConstraints must be called if you manually reposition a dd element.
17549      * @method resetConstraints
17550      * @param {boolean} maintainOffset
17551      */
17552     resetConstraints: function() {
17553
17554
17555         // Maintain offsets if necessary
17556         if (this.initPageX || this.initPageX === 0) {
17557             // figure out how much this thing has moved
17558             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17559             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17560
17561             this.setInitPosition(dx, dy);
17562
17563         // This is the first time we have detected the element's position
17564         } else {
17565             this.setInitPosition();
17566         }
17567
17568         if (this.constrainX) {
17569             this.setXConstraint( this.leftConstraint,
17570                                  this.rightConstraint,
17571                                  this.xTickSize        );
17572         }
17573
17574         if (this.constrainY) {
17575             this.setYConstraint( this.topConstraint,
17576                                  this.bottomConstraint,
17577                                  this.yTickSize         );
17578         }
17579     },
17580
17581     /**
17582      * Normally the drag element is moved pixel by pixel, but we can specify
17583      * that it move a number of pixels at a time.  This method resolves the
17584      * location when we have it set up like this.
17585      * @method getTick
17586      * @param {int} val where we want to place the object
17587      * @param {int[]} tickArray sorted array of valid points
17588      * @return {int} the closest tick
17589      * @private
17590      */
17591     getTick: function(val, tickArray) {
17592
17593         if (!tickArray) {
17594             // If tick interval is not defined, it is effectively 1 pixel,
17595             // so we return the value passed to us.
17596             return val;
17597         } else if (tickArray[0] >= val) {
17598             // The value is lower than the first tick, so we return the first
17599             // tick.
17600             return tickArray[0];
17601         } else {
17602             for (var i=0, len=tickArray.length; i<len; ++i) {
17603                 var next = i + 1;
17604                 if (tickArray[next] && tickArray[next] >= val) {
17605                     var diff1 = val - tickArray[i];
17606                     var diff2 = tickArray[next] - val;
17607                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17608                 }
17609             }
17610
17611             // The value is larger than the last tick, so we return the last
17612             // tick.
17613             return tickArray[tickArray.length - 1];
17614         }
17615     },
17616
17617     /**
17618      * toString method
17619      * @method toString
17620      * @return {string} string representation of the dd obj
17621      */
17622     toString: function() {
17623         return ("DragDrop " + this.id);
17624     }
17625
17626 });
17627
17628 })();
17629 /*
17630  * Based on:
17631  * Ext JS Library 1.1.1
17632  * Copyright(c) 2006-2007, Ext JS, LLC.
17633  *
17634  * Originally Released Under LGPL - original licence link has changed is not relivant.
17635  *
17636  * Fork - LGPL
17637  * <script type="text/javascript">
17638  */
17639
17640
17641 /**
17642  * The drag and drop utility provides a framework for building drag and drop
17643  * applications.  In addition to enabling drag and drop for specific elements,
17644  * the drag and drop elements are tracked by the manager class, and the
17645  * interactions between the various elements are tracked during the drag and
17646  * the implementing code is notified about these important moments.
17647  */
17648
17649 // Only load the library once.  Rewriting the manager class would orphan
17650 // existing drag and drop instances.
17651 if (!Roo.dd.DragDropMgr) {
17652
17653 /**
17654  * @class Roo.dd.DragDropMgr
17655  * DragDropMgr is a singleton that tracks the element interaction for
17656  * all DragDrop items in the window.  Generally, you will not call
17657  * this class directly, but it does have helper methods that could
17658  * be useful in your DragDrop implementations.
17659  * @singleton
17660  */
17661 Roo.dd.DragDropMgr = function() {
17662
17663     var Event = Roo.EventManager;
17664
17665     return {
17666
17667         /**
17668          * Two dimensional Array of registered DragDrop objects.  The first
17669          * dimension is the DragDrop item group, the second the DragDrop
17670          * object.
17671          * @property ids
17672          * @type {string: string}
17673          * @private
17674          * @static
17675          */
17676         ids: {},
17677
17678         /**
17679          * Array of element ids defined as drag handles.  Used to determine
17680          * if the element that generated the mousedown event is actually the
17681          * handle and not the html element itself.
17682          * @property handleIds
17683          * @type {string: string}
17684          * @private
17685          * @static
17686          */
17687         handleIds: {},
17688
17689         /**
17690          * the DragDrop object that is currently being dragged
17691          * @property dragCurrent
17692          * @type DragDrop
17693          * @private
17694          * @static
17695          **/
17696         dragCurrent: null,
17697
17698         /**
17699          * the DragDrop object(s) that are being hovered over
17700          * @property dragOvers
17701          * @type Array
17702          * @private
17703          * @static
17704          */
17705         dragOvers: {},
17706
17707         /**
17708          * the X distance between the cursor and the object being dragged
17709          * @property deltaX
17710          * @type int
17711          * @private
17712          * @static
17713          */
17714         deltaX: 0,
17715
17716         /**
17717          * the Y distance between the cursor and the object being dragged
17718          * @property deltaY
17719          * @type int
17720          * @private
17721          * @static
17722          */
17723         deltaY: 0,
17724
17725         /**
17726          * Flag to determine if we should prevent the default behavior of the
17727          * events we define. By default this is true, but this can be set to
17728          * false if you need the default behavior (not recommended)
17729          * @property preventDefault
17730          * @type boolean
17731          * @static
17732          */
17733         preventDefault: true,
17734
17735         /**
17736          * Flag to determine if we should stop the propagation of the events
17737          * we generate. This is true by default but you may want to set it to
17738          * false if the html element contains other features that require the
17739          * mouse click.
17740          * @property stopPropagation
17741          * @type boolean
17742          * @static
17743          */
17744         stopPropagation: true,
17745
17746         /**
17747          * Internal flag that is set to true when drag and drop has been
17748          * intialized
17749          * @property initialized
17750          * @private
17751          * @static
17752          */
17753         initalized: false,
17754
17755         /**
17756          * All drag and drop can be disabled.
17757          * @property locked
17758          * @private
17759          * @static
17760          */
17761         locked: false,
17762
17763         /**
17764          * Called the first time an element is registered.
17765          * @method init
17766          * @private
17767          * @static
17768          */
17769         init: function() {
17770             this.initialized = true;
17771         },
17772
17773         /**
17774          * In point mode, drag and drop interaction is defined by the
17775          * location of the cursor during the drag/drop
17776          * @property POINT
17777          * @type int
17778          * @static
17779          */
17780         POINT: 0,
17781
17782         /**
17783          * In intersect mode, drag and drop interactio nis defined by the
17784          * overlap of two or more drag and drop objects.
17785          * @property INTERSECT
17786          * @type int
17787          * @static
17788          */
17789         INTERSECT: 1,
17790
17791         /**
17792          * The current drag and drop mode.  Default: POINT
17793          * @property mode
17794          * @type int
17795          * @static
17796          */
17797         mode: 0,
17798
17799         /**
17800          * Runs method on all drag and drop objects
17801          * @method _execOnAll
17802          * @private
17803          * @static
17804          */
17805         _execOnAll: function(sMethod, args) {
17806             for (var i in this.ids) {
17807                 for (var j in this.ids[i]) {
17808                     var oDD = this.ids[i][j];
17809                     if (! this.isTypeOfDD(oDD)) {
17810                         continue;
17811                     }
17812                     oDD[sMethod].apply(oDD, args);
17813                 }
17814             }
17815         },
17816
17817         /**
17818          * Drag and drop initialization.  Sets up the global event handlers
17819          * @method _onLoad
17820          * @private
17821          * @static
17822          */
17823         _onLoad: function() {
17824
17825             this.init();
17826
17827             if (!Roo.isTouch) {
17828                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17829                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17830             }
17831             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17832             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17833             
17834             Event.on(window,   "unload",    this._onUnload, this, true);
17835             Event.on(window,   "resize",    this._onResize, this, true);
17836             // Event.on(window,   "mouseout",    this._test);
17837
17838         },
17839
17840         /**
17841          * Reset constraints on all drag and drop objs
17842          * @method _onResize
17843          * @private
17844          * @static
17845          */
17846         _onResize: function(e) {
17847             this._execOnAll("resetConstraints", []);
17848         },
17849
17850         /**
17851          * Lock all drag and drop functionality
17852          * @method lock
17853          * @static
17854          */
17855         lock: function() { this.locked = true; },
17856
17857         /**
17858          * Unlock all drag and drop functionality
17859          * @method unlock
17860          * @static
17861          */
17862         unlock: function() { this.locked = false; },
17863
17864         /**
17865          * Is drag and drop locked?
17866          * @method isLocked
17867          * @return {boolean} True if drag and drop is locked, false otherwise.
17868          * @static
17869          */
17870         isLocked: function() { return this.locked; },
17871
17872         /**
17873          * Location cache that is set for all drag drop objects when a drag is
17874          * initiated, cleared when the drag is finished.
17875          * @property locationCache
17876          * @private
17877          * @static
17878          */
17879         locationCache: {},
17880
17881         /**
17882          * Set useCache to false if you want to force object the lookup of each
17883          * drag and drop linked element constantly during a drag.
17884          * @property useCache
17885          * @type boolean
17886          * @static
17887          */
17888         useCache: true,
17889
17890         /**
17891          * The number of pixels that the mouse needs to move after the
17892          * mousedown before the drag is initiated.  Default=3;
17893          * @property clickPixelThresh
17894          * @type int
17895          * @static
17896          */
17897         clickPixelThresh: 3,
17898
17899         /**
17900          * The number of milliseconds after the mousedown event to initiate the
17901          * drag if we don't get a mouseup event. Default=1000
17902          * @property clickTimeThresh
17903          * @type int
17904          * @static
17905          */
17906         clickTimeThresh: 350,
17907
17908         /**
17909          * Flag that indicates that either the drag pixel threshold or the
17910          * mousdown time threshold has been met
17911          * @property dragThreshMet
17912          * @type boolean
17913          * @private
17914          * @static
17915          */
17916         dragThreshMet: false,
17917
17918         /**
17919          * Timeout used for the click time threshold
17920          * @property clickTimeout
17921          * @type Object
17922          * @private
17923          * @static
17924          */
17925         clickTimeout: null,
17926
17927         /**
17928          * The X position of the mousedown event stored for later use when a
17929          * drag threshold is met.
17930          * @property startX
17931          * @type int
17932          * @private
17933          * @static
17934          */
17935         startX: 0,
17936
17937         /**
17938          * The Y position of the mousedown event stored for later use when a
17939          * drag threshold is met.
17940          * @property startY
17941          * @type int
17942          * @private
17943          * @static
17944          */
17945         startY: 0,
17946
17947         /**
17948          * Each DragDrop instance must be registered with the DragDropMgr.
17949          * This is executed in DragDrop.init()
17950          * @method regDragDrop
17951          * @param {DragDrop} oDD the DragDrop object to register
17952          * @param {String} sGroup the name of the group this element belongs to
17953          * @static
17954          */
17955         regDragDrop: function(oDD, sGroup) {
17956             if (!this.initialized) { this.init(); }
17957
17958             if (!this.ids[sGroup]) {
17959                 this.ids[sGroup] = {};
17960             }
17961             this.ids[sGroup][oDD.id] = oDD;
17962         },
17963
17964         /**
17965          * Removes the supplied dd instance from the supplied group. Executed
17966          * by DragDrop.removeFromGroup, so don't call this function directly.
17967          * @method removeDDFromGroup
17968          * @private
17969          * @static
17970          */
17971         removeDDFromGroup: function(oDD, sGroup) {
17972             if (!this.ids[sGroup]) {
17973                 this.ids[sGroup] = {};
17974             }
17975
17976             var obj = this.ids[sGroup];
17977             if (obj && obj[oDD.id]) {
17978                 delete obj[oDD.id];
17979             }
17980         },
17981
17982         /**
17983          * Unregisters a drag and drop item.  This is executed in
17984          * DragDrop.unreg, use that method instead of calling this directly.
17985          * @method _remove
17986          * @private
17987          * @static
17988          */
17989         _remove: function(oDD) {
17990             for (var g in oDD.groups) {
17991                 if (g && this.ids[g][oDD.id]) {
17992                     delete this.ids[g][oDD.id];
17993                 }
17994             }
17995             delete this.handleIds[oDD.id];
17996         },
17997
17998         /**
17999          * Each DragDrop handle element must be registered.  This is done
18000          * automatically when executing DragDrop.setHandleElId()
18001          * @method regHandle
18002          * @param {String} sDDId the DragDrop id this element is a handle for
18003          * @param {String} sHandleId the id of the element that is the drag
18004          * handle
18005          * @static
18006          */
18007         regHandle: function(sDDId, sHandleId) {
18008             if (!this.handleIds[sDDId]) {
18009                 this.handleIds[sDDId] = {};
18010             }
18011             this.handleIds[sDDId][sHandleId] = sHandleId;
18012         },
18013
18014         /**
18015          * Utility function to determine if a given element has been
18016          * registered as a drag drop item.
18017          * @method isDragDrop
18018          * @param {String} id the element id to check
18019          * @return {boolean} true if this element is a DragDrop item,
18020          * false otherwise
18021          * @static
18022          */
18023         isDragDrop: function(id) {
18024             return ( this.getDDById(id) ) ? true : false;
18025         },
18026
18027         /**
18028          * Returns the drag and drop instances that are in all groups the
18029          * passed in instance belongs to.
18030          * @method getRelated
18031          * @param {DragDrop} p_oDD the obj to get related data for
18032          * @param {boolean} bTargetsOnly if true, only return targetable objs
18033          * @return {DragDrop[]} the related instances
18034          * @static
18035          */
18036         getRelated: function(p_oDD, bTargetsOnly) {
18037             var oDDs = [];
18038             for (var i in p_oDD.groups) {
18039                 for (j in this.ids[i]) {
18040                     var dd = this.ids[i][j];
18041                     if (! this.isTypeOfDD(dd)) {
18042                         continue;
18043                     }
18044                     if (!bTargetsOnly || dd.isTarget) {
18045                         oDDs[oDDs.length] = dd;
18046                     }
18047                 }
18048             }
18049
18050             return oDDs;
18051         },
18052
18053         /**
18054          * Returns true if the specified dd target is a legal target for
18055          * the specifice drag obj
18056          * @method isLegalTarget
18057          * @param {DragDrop} the drag obj
18058          * @param {DragDrop} the target
18059          * @return {boolean} true if the target is a legal target for the
18060          * dd obj
18061          * @static
18062          */
18063         isLegalTarget: function (oDD, oTargetDD) {
18064             var targets = this.getRelated(oDD, true);
18065             for (var i=0, len=targets.length;i<len;++i) {
18066                 if (targets[i].id == oTargetDD.id) {
18067                     return true;
18068                 }
18069             }
18070
18071             return false;
18072         },
18073
18074         /**
18075          * My goal is to be able to transparently determine if an object is
18076          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18077          * returns "object", oDD.constructor.toString() always returns
18078          * "DragDrop" and not the name of the subclass.  So for now it just
18079          * evaluates a well-known variable in DragDrop.
18080          * @method isTypeOfDD
18081          * @param {Object} the object to evaluate
18082          * @return {boolean} true if typeof oDD = DragDrop
18083          * @static
18084          */
18085         isTypeOfDD: function (oDD) {
18086             return (oDD && oDD.__ygDragDrop);
18087         },
18088
18089         /**
18090          * Utility function to determine if a given element has been
18091          * registered as a drag drop handle for the given Drag Drop object.
18092          * @method isHandle
18093          * @param {String} id the element id to check
18094          * @return {boolean} true if this element is a DragDrop handle, false
18095          * otherwise
18096          * @static
18097          */
18098         isHandle: function(sDDId, sHandleId) {
18099             return ( this.handleIds[sDDId] &&
18100                             this.handleIds[sDDId][sHandleId] );
18101         },
18102
18103         /**
18104          * Returns the DragDrop instance for a given id
18105          * @method getDDById
18106          * @param {String} id the id of the DragDrop object
18107          * @return {DragDrop} the drag drop object, null if it is not found
18108          * @static
18109          */
18110         getDDById: function(id) {
18111             for (var i in this.ids) {
18112                 if (this.ids[i][id]) {
18113                     return this.ids[i][id];
18114                 }
18115             }
18116             return null;
18117         },
18118
18119         /**
18120          * Fired after a registered DragDrop object gets the mousedown event.
18121          * Sets up the events required to track the object being dragged
18122          * @method handleMouseDown
18123          * @param {Event} e the event
18124          * @param oDD the DragDrop object being dragged
18125          * @private
18126          * @static
18127          */
18128         handleMouseDown: function(e, oDD) {
18129             if(Roo.QuickTips){
18130                 Roo.QuickTips.disable();
18131             }
18132             this.currentTarget = e.getTarget();
18133
18134             this.dragCurrent = oDD;
18135
18136             var el = oDD.getEl();
18137
18138             // track start position
18139             this.startX = e.getPageX();
18140             this.startY = e.getPageY();
18141
18142             this.deltaX = this.startX - el.offsetLeft;
18143             this.deltaY = this.startY - el.offsetTop;
18144
18145             this.dragThreshMet = false;
18146
18147             this.clickTimeout = setTimeout(
18148                     function() {
18149                         var DDM = Roo.dd.DDM;
18150                         DDM.startDrag(DDM.startX, DDM.startY);
18151                     },
18152                     this.clickTimeThresh );
18153         },
18154
18155         /**
18156          * Fired when either the drag pixel threshol or the mousedown hold
18157          * time threshold has been met.
18158          * @method startDrag
18159          * @param x {int} the X position of the original mousedown
18160          * @param y {int} the Y position of the original mousedown
18161          * @static
18162          */
18163         startDrag: function(x, y) {
18164             clearTimeout(this.clickTimeout);
18165             if (this.dragCurrent) {
18166                 this.dragCurrent.b4StartDrag(x, y);
18167                 this.dragCurrent.startDrag(x, y);
18168             }
18169             this.dragThreshMet = true;
18170         },
18171
18172         /**
18173          * Internal function to handle the mouseup event.  Will be invoked
18174          * from the context of the document.
18175          * @method handleMouseUp
18176          * @param {Event} e the event
18177          * @private
18178          * @static
18179          */
18180         handleMouseUp: function(e) {
18181
18182             if(Roo.QuickTips){
18183                 Roo.QuickTips.enable();
18184             }
18185             if (! this.dragCurrent) {
18186                 return;
18187             }
18188
18189             clearTimeout(this.clickTimeout);
18190
18191             if (this.dragThreshMet) {
18192                 this.fireEvents(e, true);
18193             } else {
18194             }
18195
18196             this.stopDrag(e);
18197
18198             this.stopEvent(e);
18199         },
18200
18201         /**
18202          * Utility to stop event propagation and event default, if these
18203          * features are turned on.
18204          * @method stopEvent
18205          * @param {Event} e the event as returned by this.getEvent()
18206          * @static
18207          */
18208         stopEvent: function(e){
18209             if(this.stopPropagation) {
18210                 e.stopPropagation();
18211             }
18212
18213             if (this.preventDefault) {
18214                 e.preventDefault();
18215             }
18216         },
18217
18218         /**
18219          * Internal function to clean up event handlers after the drag
18220          * operation is complete
18221          * @method stopDrag
18222          * @param {Event} e the event
18223          * @private
18224          * @static
18225          */
18226         stopDrag: function(e) {
18227             // Fire the drag end event for the item that was dragged
18228             if (this.dragCurrent) {
18229                 if (this.dragThreshMet) {
18230                     this.dragCurrent.b4EndDrag(e);
18231                     this.dragCurrent.endDrag(e);
18232                 }
18233
18234                 this.dragCurrent.onMouseUp(e);
18235             }
18236
18237             this.dragCurrent = null;
18238             this.dragOvers = {};
18239         },
18240
18241         /**
18242          * Internal function to handle the mousemove event.  Will be invoked
18243          * from the context of the html element.
18244          *
18245          * @TODO figure out what we can do about mouse events lost when the
18246          * user drags objects beyond the window boundary.  Currently we can
18247          * detect this in internet explorer by verifying that the mouse is
18248          * down during the mousemove event.  Firefox doesn't give us the
18249          * button state on the mousemove event.
18250          * @method handleMouseMove
18251          * @param {Event} e the event
18252          * @private
18253          * @static
18254          */
18255         handleMouseMove: function(e) {
18256             if (! this.dragCurrent) {
18257                 return true;
18258             }
18259
18260             // var button = e.which || e.button;
18261
18262             // check for IE mouseup outside of page boundary
18263             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18264                 this.stopEvent(e);
18265                 return this.handleMouseUp(e);
18266             }
18267
18268             if (!this.dragThreshMet) {
18269                 var diffX = Math.abs(this.startX - e.getPageX());
18270                 var diffY = Math.abs(this.startY - e.getPageY());
18271                 if (diffX > this.clickPixelThresh ||
18272                             diffY > this.clickPixelThresh) {
18273                     this.startDrag(this.startX, this.startY);
18274                 }
18275             }
18276
18277             if (this.dragThreshMet) {
18278                 this.dragCurrent.b4Drag(e);
18279                 this.dragCurrent.onDrag(e);
18280                 if(!this.dragCurrent.moveOnly){
18281                     this.fireEvents(e, false);
18282                 }
18283             }
18284
18285             this.stopEvent(e);
18286
18287             return true;
18288         },
18289
18290         /**
18291          * Iterates over all of the DragDrop elements to find ones we are
18292          * hovering over or dropping on
18293          * @method fireEvents
18294          * @param {Event} e the event
18295          * @param {boolean} isDrop is this a drop op or a mouseover op?
18296          * @private
18297          * @static
18298          */
18299         fireEvents: function(e, isDrop) {
18300             var dc = this.dragCurrent;
18301
18302             // If the user did the mouse up outside of the window, we could
18303             // get here even though we have ended the drag.
18304             if (!dc || dc.isLocked()) {
18305                 return;
18306             }
18307
18308             var pt = e.getPoint();
18309
18310             // cache the previous dragOver array
18311             var oldOvers = [];
18312
18313             var outEvts   = [];
18314             var overEvts  = [];
18315             var dropEvts  = [];
18316             var enterEvts = [];
18317
18318             // Check to see if the object(s) we were hovering over is no longer
18319             // being hovered over so we can fire the onDragOut event
18320             for (var i in this.dragOvers) {
18321
18322                 var ddo = this.dragOvers[i];
18323
18324                 if (! this.isTypeOfDD(ddo)) {
18325                     continue;
18326                 }
18327
18328                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18329                     outEvts.push( ddo );
18330                 }
18331
18332                 oldOvers[i] = true;
18333                 delete this.dragOvers[i];
18334             }
18335
18336             for (var sGroup in dc.groups) {
18337
18338                 if ("string" != typeof sGroup) {
18339                     continue;
18340                 }
18341
18342                 for (i in this.ids[sGroup]) {
18343                     var oDD = this.ids[sGroup][i];
18344                     if (! this.isTypeOfDD(oDD)) {
18345                         continue;
18346                     }
18347
18348                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18349                         if (this.isOverTarget(pt, oDD, this.mode)) {
18350                             // look for drop interactions
18351                             if (isDrop) {
18352                                 dropEvts.push( oDD );
18353                             // look for drag enter and drag over interactions
18354                             } else {
18355
18356                                 // initial drag over: dragEnter fires
18357                                 if (!oldOvers[oDD.id]) {
18358                                     enterEvts.push( oDD );
18359                                 // subsequent drag overs: dragOver fires
18360                                 } else {
18361                                     overEvts.push( oDD );
18362                                 }
18363
18364                                 this.dragOvers[oDD.id] = oDD;
18365                             }
18366                         }
18367                     }
18368                 }
18369             }
18370
18371             if (this.mode) {
18372                 if (outEvts.length) {
18373                     dc.b4DragOut(e, outEvts);
18374                     dc.onDragOut(e, outEvts);
18375                 }
18376
18377                 if (enterEvts.length) {
18378                     dc.onDragEnter(e, enterEvts);
18379                 }
18380
18381                 if (overEvts.length) {
18382                     dc.b4DragOver(e, overEvts);
18383                     dc.onDragOver(e, overEvts);
18384                 }
18385
18386                 if (dropEvts.length) {
18387                     dc.b4DragDrop(e, dropEvts);
18388                     dc.onDragDrop(e, dropEvts);
18389                 }
18390
18391             } else {
18392                 // fire dragout events
18393                 var len = 0;
18394                 for (i=0, len=outEvts.length; i<len; ++i) {
18395                     dc.b4DragOut(e, outEvts[i].id);
18396                     dc.onDragOut(e, outEvts[i].id);
18397                 }
18398
18399                 // fire enter events
18400                 for (i=0,len=enterEvts.length; i<len; ++i) {
18401                     // dc.b4DragEnter(e, oDD.id);
18402                     dc.onDragEnter(e, enterEvts[i].id);
18403                 }
18404
18405                 // fire over events
18406                 for (i=0,len=overEvts.length; i<len; ++i) {
18407                     dc.b4DragOver(e, overEvts[i].id);
18408                     dc.onDragOver(e, overEvts[i].id);
18409                 }
18410
18411                 // fire drop events
18412                 for (i=0, len=dropEvts.length; i<len; ++i) {
18413                     dc.b4DragDrop(e, dropEvts[i].id);
18414                     dc.onDragDrop(e, dropEvts[i].id);
18415                 }
18416
18417             }
18418
18419             // notify about a drop that did not find a target
18420             if (isDrop && !dropEvts.length) {
18421                 dc.onInvalidDrop(e);
18422             }
18423
18424         },
18425
18426         /**
18427          * Helper function for getting the best match from the list of drag
18428          * and drop objects returned by the drag and drop events when we are
18429          * in INTERSECT mode.  It returns either the first object that the
18430          * cursor is over, or the object that has the greatest overlap with
18431          * the dragged element.
18432          * @method getBestMatch
18433          * @param  {DragDrop[]} dds The array of drag and drop objects
18434          * targeted
18435          * @return {DragDrop}       The best single match
18436          * @static
18437          */
18438         getBestMatch: function(dds) {
18439             var winner = null;
18440             // Return null if the input is not what we expect
18441             //if (!dds || !dds.length || dds.length == 0) {
18442                // winner = null;
18443             // If there is only one item, it wins
18444             //} else if (dds.length == 1) {
18445
18446             var len = dds.length;
18447
18448             if (len == 1) {
18449                 winner = dds[0];
18450             } else {
18451                 // Loop through the targeted items
18452                 for (var i=0; i<len; ++i) {
18453                     var dd = dds[i];
18454                     // If the cursor is over the object, it wins.  If the
18455                     // cursor is over multiple matches, the first one we come
18456                     // to wins.
18457                     if (dd.cursorIsOver) {
18458                         winner = dd;
18459                         break;
18460                     // Otherwise the object with the most overlap wins
18461                     } else {
18462                         if (!winner ||
18463                             winner.overlap.getArea() < dd.overlap.getArea()) {
18464                             winner = dd;
18465                         }
18466                     }
18467                 }
18468             }
18469
18470             return winner;
18471         },
18472
18473         /**
18474          * Refreshes the cache of the top-left and bottom-right points of the
18475          * drag and drop objects in the specified group(s).  This is in the
18476          * format that is stored in the drag and drop instance, so typical
18477          * usage is:
18478          * <code>
18479          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18480          * </code>
18481          * Alternatively:
18482          * <code>
18483          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18484          * </code>
18485          * @TODO this really should be an indexed array.  Alternatively this
18486          * method could accept both.
18487          * @method refreshCache
18488          * @param {Object} groups an associative array of groups to refresh
18489          * @static
18490          */
18491         refreshCache: function(groups) {
18492             for (var sGroup in groups) {
18493                 if ("string" != typeof sGroup) {
18494                     continue;
18495                 }
18496                 for (var i in this.ids[sGroup]) {
18497                     var oDD = this.ids[sGroup][i];
18498
18499                     if (this.isTypeOfDD(oDD)) {
18500                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18501                         var loc = this.getLocation(oDD);
18502                         if (loc) {
18503                             this.locationCache[oDD.id] = loc;
18504                         } else {
18505                             delete this.locationCache[oDD.id];
18506                             // this will unregister the drag and drop object if
18507                             // the element is not in a usable state
18508                             // oDD.unreg();
18509                         }
18510                     }
18511                 }
18512             }
18513         },
18514
18515         /**
18516          * This checks to make sure an element exists and is in the DOM.  The
18517          * main purpose is to handle cases where innerHTML is used to remove
18518          * drag and drop objects from the DOM.  IE provides an 'unspecified
18519          * error' when trying to access the offsetParent of such an element
18520          * @method verifyEl
18521          * @param {HTMLElement} el the element to check
18522          * @return {boolean} true if the element looks usable
18523          * @static
18524          */
18525         verifyEl: function(el) {
18526             if (el) {
18527                 var parent;
18528                 if(Roo.isIE){
18529                     try{
18530                         parent = el.offsetParent;
18531                     }catch(e){}
18532                 }else{
18533                     parent = el.offsetParent;
18534                 }
18535                 if (parent) {
18536                     return true;
18537                 }
18538             }
18539
18540             return false;
18541         },
18542
18543         /**
18544          * Returns a Region object containing the drag and drop element's position
18545          * and size, including the padding configured for it
18546          * @method getLocation
18547          * @param {DragDrop} oDD the drag and drop object to get the
18548          *                       location for
18549          * @return {Roo.lib.Region} a Region object representing the total area
18550          *                             the element occupies, including any padding
18551          *                             the instance is configured for.
18552          * @static
18553          */
18554         getLocation: function(oDD) {
18555             if (! this.isTypeOfDD(oDD)) {
18556                 return null;
18557             }
18558
18559             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18560
18561             try {
18562                 pos= Roo.lib.Dom.getXY(el);
18563             } catch (e) { }
18564
18565             if (!pos) {
18566                 return null;
18567             }
18568
18569             x1 = pos[0];
18570             x2 = x1 + el.offsetWidth;
18571             y1 = pos[1];
18572             y2 = y1 + el.offsetHeight;
18573
18574             t = y1 - oDD.padding[0];
18575             r = x2 + oDD.padding[1];
18576             b = y2 + oDD.padding[2];
18577             l = x1 - oDD.padding[3];
18578
18579             return new Roo.lib.Region( t, r, b, l );
18580         },
18581
18582         /**
18583          * Checks the cursor location to see if it over the target
18584          * @method isOverTarget
18585          * @param {Roo.lib.Point} pt The point to evaluate
18586          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18587          * @return {boolean} true if the mouse is over the target
18588          * @private
18589          * @static
18590          */
18591         isOverTarget: function(pt, oTarget, intersect) {
18592             // use cache if available
18593             var loc = this.locationCache[oTarget.id];
18594             if (!loc || !this.useCache) {
18595                 loc = this.getLocation(oTarget);
18596                 this.locationCache[oTarget.id] = loc;
18597
18598             }
18599
18600             if (!loc) {
18601                 return false;
18602             }
18603
18604             oTarget.cursorIsOver = loc.contains( pt );
18605
18606             // DragDrop is using this as a sanity check for the initial mousedown
18607             // in this case we are done.  In POINT mode, if the drag obj has no
18608             // contraints, we are also done. Otherwise we need to evaluate the
18609             // location of the target as related to the actual location of the
18610             // dragged element.
18611             var dc = this.dragCurrent;
18612             if (!dc || !dc.getTargetCoord ||
18613                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18614                 return oTarget.cursorIsOver;
18615             }
18616
18617             oTarget.overlap = null;
18618
18619             // Get the current location of the drag element, this is the
18620             // location of the mouse event less the delta that represents
18621             // where the original mousedown happened on the element.  We
18622             // need to consider constraints and ticks as well.
18623             var pos = dc.getTargetCoord(pt.x, pt.y);
18624
18625             var el = dc.getDragEl();
18626             var curRegion = new Roo.lib.Region( pos.y,
18627                                                    pos.x + el.offsetWidth,
18628                                                    pos.y + el.offsetHeight,
18629                                                    pos.x );
18630
18631             var overlap = curRegion.intersect(loc);
18632
18633             if (overlap) {
18634                 oTarget.overlap = overlap;
18635                 return (intersect) ? true : oTarget.cursorIsOver;
18636             } else {
18637                 return false;
18638             }
18639         },
18640
18641         /**
18642          * unload event handler
18643          * @method _onUnload
18644          * @private
18645          * @static
18646          */
18647         _onUnload: function(e, me) {
18648             Roo.dd.DragDropMgr.unregAll();
18649         },
18650
18651         /**
18652          * Cleans up the drag and drop events and objects.
18653          * @method unregAll
18654          * @private
18655          * @static
18656          */
18657         unregAll: function() {
18658
18659             if (this.dragCurrent) {
18660                 this.stopDrag();
18661                 this.dragCurrent = null;
18662             }
18663
18664             this._execOnAll("unreg", []);
18665
18666             for (i in this.elementCache) {
18667                 delete this.elementCache[i];
18668             }
18669
18670             this.elementCache = {};
18671             this.ids = {};
18672         },
18673
18674         /**
18675          * A cache of DOM elements
18676          * @property elementCache
18677          * @private
18678          * @static
18679          */
18680         elementCache: {},
18681
18682         /**
18683          * Get the wrapper for the DOM element specified
18684          * @method getElWrapper
18685          * @param {String} id the id of the element to get
18686          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18687          * @private
18688          * @deprecated This wrapper isn't that useful
18689          * @static
18690          */
18691         getElWrapper: function(id) {
18692             var oWrapper = this.elementCache[id];
18693             if (!oWrapper || !oWrapper.el) {
18694                 oWrapper = this.elementCache[id] =
18695                     new this.ElementWrapper(Roo.getDom(id));
18696             }
18697             return oWrapper;
18698         },
18699
18700         /**
18701          * Returns the actual DOM element
18702          * @method getElement
18703          * @param {String} id the id of the elment to get
18704          * @return {Object} The element
18705          * @deprecated use Roo.getDom instead
18706          * @static
18707          */
18708         getElement: function(id) {
18709             return Roo.getDom(id);
18710         },
18711
18712         /**
18713          * Returns the style property for the DOM element (i.e.,
18714          * document.getElById(id).style)
18715          * @method getCss
18716          * @param {String} id the id of the elment to get
18717          * @return {Object} The style property of the element
18718          * @deprecated use Roo.getDom instead
18719          * @static
18720          */
18721         getCss: function(id) {
18722             var el = Roo.getDom(id);
18723             return (el) ? el.style : null;
18724         },
18725
18726         /**
18727          * Inner class for cached elements
18728          * @class DragDropMgr.ElementWrapper
18729          * @for DragDropMgr
18730          * @private
18731          * @deprecated
18732          */
18733         ElementWrapper: function(el) {
18734                 /**
18735                  * The element
18736                  * @property el
18737                  */
18738                 this.el = el || null;
18739                 /**
18740                  * The element id
18741                  * @property id
18742                  */
18743                 this.id = this.el && el.id;
18744                 /**
18745                  * A reference to the style property
18746                  * @property css
18747                  */
18748                 this.css = this.el && el.style;
18749             },
18750
18751         /**
18752          * Returns the X position of an html element
18753          * @method getPosX
18754          * @param el the element for which to get the position
18755          * @return {int} the X coordinate
18756          * @for DragDropMgr
18757          * @deprecated use Roo.lib.Dom.getX instead
18758          * @static
18759          */
18760         getPosX: function(el) {
18761             return Roo.lib.Dom.getX(el);
18762         },
18763
18764         /**
18765          * Returns the Y position of an html element
18766          * @method getPosY
18767          * @param el the element for which to get the position
18768          * @return {int} the Y coordinate
18769          * @deprecated use Roo.lib.Dom.getY instead
18770          * @static
18771          */
18772         getPosY: function(el) {
18773             return Roo.lib.Dom.getY(el);
18774         },
18775
18776         /**
18777          * Swap two nodes.  In IE, we use the native method, for others we
18778          * emulate the IE behavior
18779          * @method swapNode
18780          * @param n1 the first node to swap
18781          * @param n2 the other node to swap
18782          * @static
18783          */
18784         swapNode: function(n1, n2) {
18785             if (n1.swapNode) {
18786                 n1.swapNode(n2);
18787             } else {
18788                 var p = n2.parentNode;
18789                 var s = n2.nextSibling;
18790
18791                 if (s == n1) {
18792                     p.insertBefore(n1, n2);
18793                 } else if (n2 == n1.nextSibling) {
18794                     p.insertBefore(n2, n1);
18795                 } else {
18796                     n1.parentNode.replaceChild(n2, n1);
18797                     p.insertBefore(n1, s);
18798                 }
18799             }
18800         },
18801
18802         /**
18803          * Returns the current scroll position
18804          * @method getScroll
18805          * @private
18806          * @static
18807          */
18808         getScroll: function () {
18809             var t, l, dde=document.documentElement, db=document.body;
18810             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18811                 t = dde.scrollTop;
18812                 l = dde.scrollLeft;
18813             } else if (db) {
18814                 t = db.scrollTop;
18815                 l = db.scrollLeft;
18816             } else {
18817
18818             }
18819             return { top: t, left: l };
18820         },
18821
18822         /**
18823          * Returns the specified element style property
18824          * @method getStyle
18825          * @param {HTMLElement} el          the element
18826          * @param {string}      styleProp   the style property
18827          * @return {string} The value of the style property
18828          * @deprecated use Roo.lib.Dom.getStyle
18829          * @static
18830          */
18831         getStyle: function(el, styleProp) {
18832             return Roo.fly(el).getStyle(styleProp);
18833         },
18834
18835         /**
18836          * Gets the scrollTop
18837          * @method getScrollTop
18838          * @return {int} the document's scrollTop
18839          * @static
18840          */
18841         getScrollTop: function () { return this.getScroll().top; },
18842
18843         /**
18844          * Gets the scrollLeft
18845          * @method getScrollLeft
18846          * @return {int} the document's scrollTop
18847          * @static
18848          */
18849         getScrollLeft: function () { return this.getScroll().left; },
18850
18851         /**
18852          * Sets the x/y position of an element to the location of the
18853          * target element.
18854          * @method moveToEl
18855          * @param {HTMLElement} moveEl      The element to move
18856          * @param {HTMLElement} targetEl    The position reference element
18857          * @static
18858          */
18859         moveToEl: function (moveEl, targetEl) {
18860             var aCoord = Roo.lib.Dom.getXY(targetEl);
18861             Roo.lib.Dom.setXY(moveEl, aCoord);
18862         },
18863
18864         /**
18865          * Numeric array sort function
18866          * @method numericSort
18867          * @static
18868          */
18869         numericSort: function(a, b) { return (a - b); },
18870
18871         /**
18872          * Internal counter
18873          * @property _timeoutCount
18874          * @private
18875          * @static
18876          */
18877         _timeoutCount: 0,
18878
18879         /**
18880          * Trying to make the load order less important.  Without this we get
18881          * an error if this file is loaded before the Event Utility.
18882          * @method _addListeners
18883          * @private
18884          * @static
18885          */
18886         _addListeners: function() {
18887             var DDM = Roo.dd.DDM;
18888             if ( Roo.lib.Event && document ) {
18889                 DDM._onLoad();
18890             } else {
18891                 if (DDM._timeoutCount > 2000) {
18892                 } else {
18893                     setTimeout(DDM._addListeners, 10);
18894                     if (document && document.body) {
18895                         DDM._timeoutCount += 1;
18896                     }
18897                 }
18898             }
18899         },
18900
18901         /**
18902          * Recursively searches the immediate parent and all child nodes for
18903          * the handle element in order to determine wheter or not it was
18904          * clicked.
18905          * @method handleWasClicked
18906          * @param node the html element to inspect
18907          * @static
18908          */
18909         handleWasClicked: function(node, id) {
18910             if (this.isHandle(id, node.id)) {
18911                 return true;
18912             } else {
18913                 // check to see if this is a text node child of the one we want
18914                 var p = node.parentNode;
18915
18916                 while (p) {
18917                     if (this.isHandle(id, p.id)) {
18918                         return true;
18919                     } else {
18920                         p = p.parentNode;
18921                     }
18922                 }
18923             }
18924
18925             return false;
18926         }
18927
18928     };
18929
18930 }();
18931
18932 // shorter alias, save a few bytes
18933 Roo.dd.DDM = Roo.dd.DragDropMgr;
18934 Roo.dd.DDM._addListeners();
18935
18936 }/*
18937  * Based on:
18938  * Ext JS Library 1.1.1
18939  * Copyright(c) 2006-2007, Ext JS, LLC.
18940  *
18941  * Originally Released Under LGPL - original licence link has changed is not relivant.
18942  *
18943  * Fork - LGPL
18944  * <script type="text/javascript">
18945  */
18946
18947 /**
18948  * @class Roo.dd.DD
18949  * A DragDrop implementation where the linked element follows the
18950  * mouse cursor during a drag.
18951  * @extends Roo.dd.DragDrop
18952  * @constructor
18953  * @param {String} id the id of the linked element
18954  * @param {String} sGroup the group of related DragDrop items
18955  * @param {object} config an object containing configurable attributes
18956  *                Valid properties for DD:
18957  *                    scroll
18958  */
18959 Roo.dd.DD = function(id, sGroup, config) {
18960     if (id) {
18961         this.init(id, sGroup, config);
18962     }
18963 };
18964
18965 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18966
18967     /**
18968      * When set to true, the utility automatically tries to scroll the browser
18969      * window wehn a drag and drop element is dragged near the viewport boundary.
18970      * Defaults to true.
18971      * @property scroll
18972      * @type boolean
18973      */
18974     scroll: true,
18975
18976     /**
18977      * Sets the pointer offset to the distance between the linked element's top
18978      * left corner and the location the element was clicked
18979      * @method autoOffset
18980      * @param {int} iPageX the X coordinate of the click
18981      * @param {int} iPageY the Y coordinate of the click
18982      */
18983     autoOffset: function(iPageX, iPageY) {
18984         var x = iPageX - this.startPageX;
18985         var y = iPageY - this.startPageY;
18986         this.setDelta(x, y);
18987     },
18988
18989     /**
18990      * Sets the pointer offset.  You can call this directly to force the
18991      * offset to be in a particular location (e.g., pass in 0,0 to set it
18992      * to the center of the object)
18993      * @method setDelta
18994      * @param {int} iDeltaX the distance from the left
18995      * @param {int} iDeltaY the distance from the top
18996      */
18997     setDelta: function(iDeltaX, iDeltaY) {
18998         this.deltaX = iDeltaX;
18999         this.deltaY = iDeltaY;
19000     },
19001
19002     /**
19003      * Sets the drag element to the location of the mousedown or click event,
19004      * maintaining the cursor location relative to the location on the element
19005      * that was clicked.  Override this if you want to place the element in a
19006      * location other than where the cursor is.
19007      * @method setDragElPos
19008      * @param {int} iPageX the X coordinate of the mousedown or drag event
19009      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19010      */
19011     setDragElPos: function(iPageX, iPageY) {
19012         // the first time we do this, we are going to check to make sure
19013         // the element has css positioning
19014
19015         var el = this.getDragEl();
19016         this.alignElWithMouse(el, iPageX, iPageY);
19017     },
19018
19019     /**
19020      * Sets the element to the location of the mousedown or click event,
19021      * maintaining the cursor location relative to the location on the element
19022      * that was clicked.  Override this if you want to place the element in a
19023      * location other than where the cursor is.
19024      * @method alignElWithMouse
19025      * @param {HTMLElement} el the element to move
19026      * @param {int} iPageX the X coordinate of the mousedown or drag event
19027      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19028      */
19029     alignElWithMouse: function(el, iPageX, iPageY) {
19030         var oCoord = this.getTargetCoord(iPageX, iPageY);
19031         var fly = el.dom ? el : Roo.fly(el);
19032         if (!this.deltaSetXY) {
19033             var aCoord = [oCoord.x, oCoord.y];
19034             fly.setXY(aCoord);
19035             var newLeft = fly.getLeft(true);
19036             var newTop  = fly.getTop(true);
19037             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19038         } else {
19039             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19040         }
19041
19042         this.cachePosition(oCoord.x, oCoord.y);
19043         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19044         return oCoord;
19045     },
19046
19047     /**
19048      * Saves the most recent position so that we can reset the constraints and
19049      * tick marks on-demand.  We need to know this so that we can calculate the
19050      * number of pixels the element is offset from its original position.
19051      * @method cachePosition
19052      * @param iPageX the current x position (optional, this just makes it so we
19053      * don't have to look it up again)
19054      * @param iPageY the current y position (optional, this just makes it so we
19055      * don't have to look it up again)
19056      */
19057     cachePosition: function(iPageX, iPageY) {
19058         if (iPageX) {
19059             this.lastPageX = iPageX;
19060             this.lastPageY = iPageY;
19061         } else {
19062             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19063             this.lastPageX = aCoord[0];
19064             this.lastPageY = aCoord[1];
19065         }
19066     },
19067
19068     /**
19069      * Auto-scroll the window if the dragged object has been moved beyond the
19070      * visible window boundary.
19071      * @method autoScroll
19072      * @param {int} x the drag element's x position
19073      * @param {int} y the drag element's y position
19074      * @param {int} h the height of the drag element
19075      * @param {int} w the width of the drag element
19076      * @private
19077      */
19078     autoScroll: function(x, y, h, w) {
19079
19080         if (this.scroll) {
19081             // The client height
19082             var clientH = Roo.lib.Dom.getViewWidth();
19083
19084             // The client width
19085             var clientW = Roo.lib.Dom.getViewHeight();
19086
19087             // The amt scrolled down
19088             var st = this.DDM.getScrollTop();
19089
19090             // The amt scrolled right
19091             var sl = this.DDM.getScrollLeft();
19092
19093             // Location of the bottom of the element
19094             var bot = h + y;
19095
19096             // Location of the right of the element
19097             var right = w + x;
19098
19099             // The distance from the cursor to the bottom of the visible area,
19100             // adjusted so that we don't scroll if the cursor is beyond the
19101             // element drag constraints
19102             var toBot = (clientH + st - y - this.deltaY);
19103
19104             // The distance from the cursor to the right of the visible area
19105             var toRight = (clientW + sl - x - this.deltaX);
19106
19107
19108             // How close to the edge the cursor must be before we scroll
19109             // var thresh = (document.all) ? 100 : 40;
19110             var thresh = 40;
19111
19112             // How many pixels to scroll per autoscroll op.  This helps to reduce
19113             // clunky scrolling. IE is more sensitive about this ... it needs this
19114             // value to be higher.
19115             var scrAmt = (document.all) ? 80 : 30;
19116
19117             // Scroll down if we are near the bottom of the visible page and the
19118             // obj extends below the crease
19119             if ( bot > clientH && toBot < thresh ) {
19120                 window.scrollTo(sl, st + scrAmt);
19121             }
19122
19123             // Scroll up if the window is scrolled down and the top of the object
19124             // goes above the top border
19125             if ( y < st && st > 0 && y - st < thresh ) {
19126                 window.scrollTo(sl, st - scrAmt);
19127             }
19128
19129             // Scroll right if the obj is beyond the right border and the cursor is
19130             // near the border.
19131             if ( right > clientW && toRight < thresh ) {
19132                 window.scrollTo(sl + scrAmt, st);
19133             }
19134
19135             // Scroll left if the window has been scrolled to the right and the obj
19136             // extends past the left border
19137             if ( x < sl && sl > 0 && x - sl < thresh ) {
19138                 window.scrollTo(sl - scrAmt, st);
19139             }
19140         }
19141     },
19142
19143     /**
19144      * Finds the location the element should be placed if we want to move
19145      * it to where the mouse location less the click offset would place us.
19146      * @method getTargetCoord
19147      * @param {int} iPageX the X coordinate of the click
19148      * @param {int} iPageY the Y coordinate of the click
19149      * @return an object that contains the coordinates (Object.x and Object.y)
19150      * @private
19151      */
19152     getTargetCoord: function(iPageX, iPageY) {
19153
19154
19155         var x = iPageX - this.deltaX;
19156         var y = iPageY - this.deltaY;
19157
19158         if (this.constrainX) {
19159             if (x < this.minX) { x = this.minX; }
19160             if (x > this.maxX) { x = this.maxX; }
19161         }
19162
19163         if (this.constrainY) {
19164             if (y < this.minY) { y = this.minY; }
19165             if (y > this.maxY) { y = this.maxY; }
19166         }
19167
19168         x = this.getTick(x, this.xTicks);
19169         y = this.getTick(y, this.yTicks);
19170
19171
19172         return {x:x, y:y};
19173     },
19174
19175     /*
19176      * Sets up config options specific to this class. Overrides
19177      * Roo.dd.DragDrop, but all versions of this method through the
19178      * inheritance chain are called
19179      */
19180     applyConfig: function() {
19181         Roo.dd.DD.superclass.applyConfig.call(this);
19182         this.scroll = (this.config.scroll !== false);
19183     },
19184
19185     /*
19186      * Event that fires prior to the onMouseDown event.  Overrides
19187      * Roo.dd.DragDrop.
19188      */
19189     b4MouseDown: function(e) {
19190         // this.resetConstraints();
19191         this.autoOffset(e.getPageX(),
19192                             e.getPageY());
19193     },
19194
19195     /*
19196      * Event that fires prior to the onDrag event.  Overrides
19197      * Roo.dd.DragDrop.
19198      */
19199     b4Drag: function(e) {
19200         this.setDragElPos(e.getPageX(),
19201                             e.getPageY());
19202     },
19203
19204     toString: function() {
19205         return ("DD " + this.id);
19206     }
19207
19208     //////////////////////////////////////////////////////////////////////////
19209     // Debugging ygDragDrop events that can be overridden
19210     //////////////////////////////////////////////////////////////////////////
19211     /*
19212     startDrag: function(x, y) {
19213     },
19214
19215     onDrag: function(e) {
19216     },
19217
19218     onDragEnter: function(e, id) {
19219     },
19220
19221     onDragOver: function(e, id) {
19222     },
19223
19224     onDragOut: function(e, id) {
19225     },
19226
19227     onDragDrop: function(e, id) {
19228     },
19229
19230     endDrag: function(e) {
19231     }
19232
19233     */
19234
19235 });/*
19236  * Based on:
19237  * Ext JS Library 1.1.1
19238  * Copyright(c) 2006-2007, Ext JS, LLC.
19239  *
19240  * Originally Released Under LGPL - original licence link has changed is not relivant.
19241  *
19242  * Fork - LGPL
19243  * <script type="text/javascript">
19244  */
19245
19246 /**
19247  * @class Roo.dd.DDProxy
19248  * A DragDrop implementation that inserts an empty, bordered div into
19249  * the document that follows the cursor during drag operations.  At the time of
19250  * the click, the frame div is resized to the dimensions of the linked html
19251  * element, and moved to the exact location of the linked element.
19252  *
19253  * References to the "frame" element refer to the single proxy element that
19254  * was created to be dragged in place of all DDProxy elements on the
19255  * page.
19256  *
19257  * @extends Roo.dd.DD
19258  * @constructor
19259  * @param {String} id the id of the linked html element
19260  * @param {String} sGroup the group of related DragDrop objects
19261  * @param {object} config an object containing configurable attributes
19262  *                Valid properties for DDProxy in addition to those in DragDrop:
19263  *                   resizeFrame, centerFrame, dragElId
19264  */
19265 Roo.dd.DDProxy = function(id, sGroup, config) {
19266     if (id) {
19267         this.init(id, sGroup, config);
19268         this.initFrame();
19269     }
19270 };
19271
19272 /**
19273  * The default drag frame div id
19274  * @property Roo.dd.DDProxy.dragElId
19275  * @type String
19276  * @static
19277  */
19278 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19279
19280 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19281
19282     /**
19283      * By default we resize the drag frame to be the same size as the element
19284      * we want to drag (this is to get the frame effect).  We can turn it off
19285      * if we want a different behavior.
19286      * @property resizeFrame
19287      * @type boolean
19288      */
19289     resizeFrame: true,
19290
19291     /**
19292      * By default the frame is positioned exactly where the drag element is, so
19293      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19294      * you do not have constraints on the obj is to have the drag frame centered
19295      * around the cursor.  Set centerFrame to true for this effect.
19296      * @property centerFrame
19297      * @type boolean
19298      */
19299     centerFrame: false,
19300
19301     /**
19302      * Creates the proxy element if it does not yet exist
19303      * @method createFrame
19304      */
19305     createFrame: function() {
19306         var self = this;
19307         var body = document.body;
19308
19309         if (!body || !body.firstChild) {
19310             setTimeout( function() { self.createFrame(); }, 50 );
19311             return;
19312         }
19313
19314         var div = this.getDragEl();
19315
19316         if (!div) {
19317             div    = document.createElement("div");
19318             div.id = this.dragElId;
19319             var s  = div.style;
19320
19321             s.position   = "absolute";
19322             s.visibility = "hidden";
19323             s.cursor     = "move";
19324             s.border     = "2px solid #aaa";
19325             s.zIndex     = 999;
19326
19327             // appendChild can blow up IE if invoked prior to the window load event
19328             // while rendering a table.  It is possible there are other scenarios
19329             // that would cause this to happen as well.
19330             body.insertBefore(div, body.firstChild);
19331         }
19332     },
19333
19334     /**
19335      * Initialization for the drag frame element.  Must be called in the
19336      * constructor of all subclasses
19337      * @method initFrame
19338      */
19339     initFrame: function() {
19340         this.createFrame();
19341     },
19342
19343     applyConfig: function() {
19344         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19345
19346         this.resizeFrame = (this.config.resizeFrame !== false);
19347         this.centerFrame = (this.config.centerFrame);
19348         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19349     },
19350
19351     /**
19352      * Resizes the drag frame to the dimensions of the clicked object, positions
19353      * it over the object, and finally displays it
19354      * @method showFrame
19355      * @param {int} iPageX X click position
19356      * @param {int} iPageY Y click position
19357      * @private
19358      */
19359     showFrame: function(iPageX, iPageY) {
19360         var el = this.getEl();
19361         var dragEl = this.getDragEl();
19362         var s = dragEl.style;
19363
19364         this._resizeProxy();
19365
19366         if (this.centerFrame) {
19367             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19368                            Math.round(parseInt(s.height, 10)/2) );
19369         }
19370
19371         this.setDragElPos(iPageX, iPageY);
19372
19373         Roo.fly(dragEl).show();
19374     },
19375
19376     /**
19377      * The proxy is automatically resized to the dimensions of the linked
19378      * element when a drag is initiated, unless resizeFrame is set to false
19379      * @method _resizeProxy
19380      * @private
19381      */
19382     _resizeProxy: function() {
19383         if (this.resizeFrame) {
19384             var el = this.getEl();
19385             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19386         }
19387     },
19388
19389     // overrides Roo.dd.DragDrop
19390     b4MouseDown: function(e) {
19391         var x = e.getPageX();
19392         var y = e.getPageY();
19393         this.autoOffset(x, y);
19394         this.setDragElPos(x, y);
19395     },
19396
19397     // overrides Roo.dd.DragDrop
19398     b4StartDrag: function(x, y) {
19399         // show the drag frame
19400         this.showFrame(x, y);
19401     },
19402
19403     // overrides Roo.dd.DragDrop
19404     b4EndDrag: function(e) {
19405         Roo.fly(this.getDragEl()).hide();
19406     },
19407
19408     // overrides Roo.dd.DragDrop
19409     // By default we try to move the element to the last location of the frame.
19410     // This is so that the default behavior mirrors that of Roo.dd.DD.
19411     endDrag: function(e) {
19412
19413         var lel = this.getEl();
19414         var del = this.getDragEl();
19415
19416         // Show the drag frame briefly so we can get its position
19417         del.style.visibility = "";
19418
19419         this.beforeMove();
19420         // Hide the linked element before the move to get around a Safari
19421         // rendering bug.
19422         lel.style.visibility = "hidden";
19423         Roo.dd.DDM.moveToEl(lel, del);
19424         del.style.visibility = "hidden";
19425         lel.style.visibility = "";
19426
19427         this.afterDrag();
19428     },
19429
19430     beforeMove : function(){
19431
19432     },
19433
19434     afterDrag : function(){
19435
19436     },
19437
19438     toString: function() {
19439         return ("DDProxy " + this.id);
19440     }
19441
19442 });
19443 /*
19444  * Based on:
19445  * Ext JS Library 1.1.1
19446  * Copyright(c) 2006-2007, Ext JS, LLC.
19447  *
19448  * Originally Released Under LGPL - original licence link has changed is not relivant.
19449  *
19450  * Fork - LGPL
19451  * <script type="text/javascript">
19452  */
19453
19454  /**
19455  * @class Roo.dd.DDTarget
19456  * A DragDrop implementation that does not move, but can be a drop
19457  * target.  You would get the same result by simply omitting implementation
19458  * for the event callbacks, but this way we reduce the processing cost of the
19459  * event listener and the callbacks.
19460  * @extends Roo.dd.DragDrop
19461  * @constructor
19462  * @param {String} id the id of the element that is a drop target
19463  * @param {String} sGroup the group of related DragDrop objects
19464  * @param {object} config an object containing configurable attributes
19465  *                 Valid properties for DDTarget in addition to those in
19466  *                 DragDrop:
19467  *                    none
19468  */
19469 Roo.dd.DDTarget = function(id, sGroup, config) {
19470     if (id) {
19471         this.initTarget(id, sGroup, config);
19472     }
19473     if (config.listeners || config.events) { 
19474        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19475             listeners : config.listeners || {}, 
19476             events : config.events || {} 
19477         });    
19478     }
19479 };
19480
19481 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19482 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19483     toString: function() {
19484         return ("DDTarget " + this.id);
19485     }
19486 });
19487 /*
19488  * Based on:
19489  * Ext JS Library 1.1.1
19490  * Copyright(c) 2006-2007, Ext JS, LLC.
19491  *
19492  * Originally Released Under LGPL - original licence link has changed is not relivant.
19493  *
19494  * Fork - LGPL
19495  * <script type="text/javascript">
19496  */
19497  
19498
19499 /**
19500  * @class Roo.dd.ScrollManager
19501  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19502  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19503  * @singleton
19504  */
19505 Roo.dd.ScrollManager = function(){
19506     var ddm = Roo.dd.DragDropMgr;
19507     var els = {};
19508     var dragEl = null;
19509     var proc = {};
19510     
19511     
19512     
19513     var onStop = function(e){
19514         dragEl = null;
19515         clearProc();
19516     };
19517     
19518     var triggerRefresh = function(){
19519         if(ddm.dragCurrent){
19520              ddm.refreshCache(ddm.dragCurrent.groups);
19521         }
19522     };
19523     
19524     var doScroll = function(){
19525         if(ddm.dragCurrent){
19526             var dds = Roo.dd.ScrollManager;
19527             if(!dds.animate){
19528                 if(proc.el.scroll(proc.dir, dds.increment)){
19529                     triggerRefresh();
19530                 }
19531             }else{
19532                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19533             }
19534         }
19535     };
19536     
19537     var clearProc = function(){
19538         if(proc.id){
19539             clearInterval(proc.id);
19540         }
19541         proc.id = 0;
19542         proc.el = null;
19543         proc.dir = "";
19544     };
19545     
19546     var startProc = function(el, dir){
19547          Roo.log('scroll startproc');
19548         clearProc();
19549         proc.el = el;
19550         proc.dir = dir;
19551         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19552     };
19553     
19554     var onFire = function(e, isDrop){
19555        
19556         if(isDrop || !ddm.dragCurrent){ return; }
19557         var dds = Roo.dd.ScrollManager;
19558         if(!dragEl || dragEl != ddm.dragCurrent){
19559             dragEl = ddm.dragCurrent;
19560             // refresh regions on drag start
19561             dds.refreshCache();
19562         }
19563         
19564         var xy = Roo.lib.Event.getXY(e);
19565         var pt = new Roo.lib.Point(xy[0], xy[1]);
19566         for(var id in els){
19567             var el = els[id], r = el._region;
19568             if(r && r.contains(pt) && el.isScrollable()){
19569                 if(r.bottom - pt.y <= dds.thresh){
19570                     if(proc.el != el){
19571                         startProc(el, "down");
19572                     }
19573                     return;
19574                 }else if(r.right - pt.x <= dds.thresh){
19575                     if(proc.el != el){
19576                         startProc(el, "left");
19577                     }
19578                     return;
19579                 }else if(pt.y - r.top <= dds.thresh){
19580                     if(proc.el != el){
19581                         startProc(el, "up");
19582                     }
19583                     return;
19584                 }else if(pt.x - r.left <= dds.thresh){
19585                     if(proc.el != el){
19586                         startProc(el, "right");
19587                     }
19588                     return;
19589                 }
19590             }
19591         }
19592         clearProc();
19593     };
19594     
19595     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19596     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19597     
19598     return {
19599         /**
19600          * Registers new overflow element(s) to auto scroll
19601          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19602          */
19603         register : function(el){
19604             if(el instanceof Array){
19605                 for(var i = 0, len = el.length; i < len; i++) {
19606                         this.register(el[i]);
19607                 }
19608             }else{
19609                 el = Roo.get(el);
19610                 els[el.id] = el;
19611             }
19612             Roo.dd.ScrollManager.els = els;
19613         },
19614         
19615         /**
19616          * Unregisters overflow element(s) so they are no longer scrolled
19617          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19618          */
19619         unregister : function(el){
19620             if(el instanceof Array){
19621                 for(var i = 0, len = el.length; i < len; i++) {
19622                         this.unregister(el[i]);
19623                 }
19624             }else{
19625                 el = Roo.get(el);
19626                 delete els[el.id];
19627             }
19628         },
19629         
19630         /**
19631          * The number of pixels from the edge of a container the pointer needs to be to 
19632          * trigger scrolling (defaults to 25)
19633          * @type Number
19634          */
19635         thresh : 25,
19636         
19637         /**
19638          * The number of pixels to scroll in each scroll increment (defaults to 50)
19639          * @type Number
19640          */
19641         increment : 100,
19642         
19643         /**
19644          * The frequency of scrolls in milliseconds (defaults to 500)
19645          * @type Number
19646          */
19647         frequency : 500,
19648         
19649         /**
19650          * True to animate the scroll (defaults to true)
19651          * @type Boolean
19652          */
19653         animate: true,
19654         
19655         /**
19656          * The animation duration in seconds - 
19657          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19658          * @type Number
19659          */
19660         animDuration: .4,
19661         
19662         /**
19663          * Manually trigger a cache refresh.
19664          */
19665         refreshCache : function(){
19666             for(var id in els){
19667                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19668                     els[id]._region = els[id].getRegion();
19669                 }
19670             }
19671         }
19672     };
19673 }();/*
19674  * Based on:
19675  * Ext JS Library 1.1.1
19676  * Copyright(c) 2006-2007, Ext JS, LLC.
19677  *
19678  * Originally Released Under LGPL - original licence link has changed is not relivant.
19679  *
19680  * Fork - LGPL
19681  * <script type="text/javascript">
19682  */
19683  
19684
19685 /**
19686  * @class Roo.dd.Registry
19687  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19688  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19689  * @singleton
19690  */
19691 Roo.dd.Registry = function(){
19692     var elements = {}; 
19693     var handles = {}; 
19694     var autoIdSeed = 0;
19695
19696     var getId = function(el, autogen){
19697         if(typeof el == "string"){
19698             return el;
19699         }
19700         var id = el.id;
19701         if(!id && autogen !== false){
19702             id = "roodd-" + (++autoIdSeed);
19703             el.id = id;
19704         }
19705         return id;
19706     };
19707     
19708     return {
19709     /**
19710      * Register a drag drop element
19711      * @param {String|HTMLElement} element The id or DOM node to register
19712      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19713      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19714      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19715      * populated in the data object (if applicable):
19716      * <pre>
19717 Value      Description<br />
19718 ---------  ------------------------------------------<br />
19719 handles    Array of DOM nodes that trigger dragging<br />
19720            for the element being registered<br />
19721 isHandle   True if the element passed in triggers<br />
19722            dragging itself, else false
19723 </pre>
19724      */
19725         register : function(el, data){
19726             data = data || {};
19727             if(typeof el == "string"){
19728                 el = document.getElementById(el);
19729             }
19730             data.ddel = el;
19731             elements[getId(el)] = data;
19732             if(data.isHandle !== false){
19733                 handles[data.ddel.id] = data;
19734             }
19735             if(data.handles){
19736                 var hs = data.handles;
19737                 for(var i = 0, len = hs.length; i < len; i++){
19738                         handles[getId(hs[i])] = data;
19739                 }
19740             }
19741         },
19742
19743     /**
19744      * Unregister a drag drop element
19745      * @param {String|HTMLElement}  element The id or DOM node to unregister
19746      */
19747         unregister : function(el){
19748             var id = getId(el, false);
19749             var data = elements[id];
19750             if(data){
19751                 delete elements[id];
19752                 if(data.handles){
19753                     var hs = data.handles;
19754                     for(var i = 0, len = hs.length; i < len; i++){
19755                         delete handles[getId(hs[i], false)];
19756                     }
19757                 }
19758             }
19759         },
19760
19761     /**
19762      * Returns the handle registered for a DOM Node by id
19763      * @param {String|HTMLElement} id The DOM node or id to look up
19764      * @return {Object} handle The custom handle data
19765      */
19766         getHandle : function(id){
19767             if(typeof id != "string"){ // must be element?
19768                 id = id.id;
19769             }
19770             return handles[id];
19771         },
19772
19773     /**
19774      * Returns the handle that is registered for the DOM node that is the target of the event
19775      * @param {Event} e The event
19776      * @return {Object} handle The custom handle data
19777      */
19778         getHandleFromEvent : function(e){
19779             var t = Roo.lib.Event.getTarget(e);
19780             return t ? handles[t.id] : null;
19781         },
19782
19783     /**
19784      * Returns a custom data object that is registered for a DOM node by id
19785      * @param {String|HTMLElement} id The DOM node or id to look up
19786      * @return {Object} data The custom data
19787      */
19788         getTarget : function(id){
19789             if(typeof id != "string"){ // must be element?
19790                 id = id.id;
19791             }
19792             return elements[id];
19793         },
19794
19795     /**
19796      * Returns a custom data object that is registered for the DOM node that is the target of the event
19797      * @param {Event} e The event
19798      * @return {Object} data The custom data
19799      */
19800         getTargetFromEvent : function(e){
19801             var t = Roo.lib.Event.getTarget(e);
19802             return t ? elements[t.id] || handles[t.id] : null;
19803         }
19804     };
19805 }();/*
19806  * Based on:
19807  * Ext JS Library 1.1.1
19808  * Copyright(c) 2006-2007, Ext JS, LLC.
19809  *
19810  * Originally Released Under LGPL - original licence link has changed is not relivant.
19811  *
19812  * Fork - LGPL
19813  * <script type="text/javascript">
19814  */
19815  
19816
19817 /**
19818  * @class Roo.dd.StatusProxy
19819  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19820  * default drag proxy used by all Roo.dd components.
19821  * @constructor
19822  * @param {Object} config
19823  */
19824 Roo.dd.StatusProxy = function(config){
19825     Roo.apply(this, config);
19826     this.id = this.id || Roo.id();
19827     this.el = new Roo.Layer({
19828         dh: {
19829             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19830                 {tag: "div", cls: "x-dd-drop-icon"},
19831                 {tag: "div", cls: "x-dd-drag-ghost"}
19832             ]
19833         }, 
19834         shadow: !config || config.shadow !== false
19835     });
19836     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19837     this.dropStatus = this.dropNotAllowed;
19838 };
19839
19840 Roo.dd.StatusProxy.prototype = {
19841     /**
19842      * @cfg {String} dropAllowed
19843      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19844      */
19845     dropAllowed : "x-dd-drop-ok",
19846     /**
19847      * @cfg {String} dropNotAllowed
19848      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19849      */
19850     dropNotAllowed : "x-dd-drop-nodrop",
19851
19852     /**
19853      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19854      * over the current target element.
19855      * @param {String} cssClass The css class for the new drop status indicator image
19856      */
19857     setStatus : function(cssClass){
19858         cssClass = cssClass || this.dropNotAllowed;
19859         if(this.dropStatus != cssClass){
19860             this.el.replaceClass(this.dropStatus, cssClass);
19861             this.dropStatus = cssClass;
19862         }
19863     },
19864
19865     /**
19866      * Resets the status indicator to the default dropNotAllowed value
19867      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19868      */
19869     reset : function(clearGhost){
19870         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19871         this.dropStatus = this.dropNotAllowed;
19872         if(clearGhost){
19873             this.ghost.update("");
19874         }
19875     },
19876
19877     /**
19878      * Updates the contents of the ghost element
19879      * @param {String} html The html that will replace the current innerHTML of the ghost element
19880      */
19881     update : function(html){
19882         if(typeof html == "string"){
19883             this.ghost.update(html);
19884         }else{
19885             this.ghost.update("");
19886             html.style.margin = "0";
19887             this.ghost.dom.appendChild(html);
19888         }
19889         // ensure float = none set?? cant remember why though.
19890         var el = this.ghost.dom.firstChild;
19891                 if(el){
19892                         Roo.fly(el).setStyle('float', 'none');
19893                 }
19894     },
19895     
19896     /**
19897      * Returns the underlying proxy {@link Roo.Layer}
19898      * @return {Roo.Layer} el
19899     */
19900     getEl : function(){
19901         return this.el;
19902     },
19903
19904     /**
19905      * Returns the ghost element
19906      * @return {Roo.Element} el
19907      */
19908     getGhost : function(){
19909         return this.ghost;
19910     },
19911
19912     /**
19913      * Hides the proxy
19914      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19915      */
19916     hide : function(clear){
19917         this.el.hide();
19918         if(clear){
19919             this.reset(true);
19920         }
19921     },
19922
19923     /**
19924      * Stops the repair animation if it's currently running
19925      */
19926     stop : function(){
19927         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19928             this.anim.stop();
19929         }
19930     },
19931
19932     /**
19933      * Displays this proxy
19934      */
19935     show : function(){
19936         this.el.show();
19937     },
19938
19939     /**
19940      * Force the Layer to sync its shadow and shim positions to the element
19941      */
19942     sync : function(){
19943         this.el.sync();
19944     },
19945
19946     /**
19947      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19948      * invalid drop operation by the item being dragged.
19949      * @param {Array} xy The XY position of the element ([x, y])
19950      * @param {Function} callback The function to call after the repair is complete
19951      * @param {Object} scope The scope in which to execute the callback
19952      */
19953     repair : function(xy, callback, scope){
19954         this.callback = callback;
19955         this.scope = scope;
19956         if(xy && this.animRepair !== false){
19957             this.el.addClass("x-dd-drag-repair");
19958             this.el.hideUnders(true);
19959             this.anim = this.el.shift({
19960                 duration: this.repairDuration || .5,
19961                 easing: 'easeOut',
19962                 xy: xy,
19963                 stopFx: true,
19964                 callback: this.afterRepair,
19965                 scope: this
19966             });
19967         }else{
19968             this.afterRepair();
19969         }
19970     },
19971
19972     // private
19973     afterRepair : function(){
19974         this.hide(true);
19975         if(typeof this.callback == "function"){
19976             this.callback.call(this.scope || this);
19977         }
19978         this.callback = null;
19979         this.scope = null;
19980     }
19981 };/*
19982  * Based on:
19983  * Ext JS Library 1.1.1
19984  * Copyright(c) 2006-2007, Ext JS, LLC.
19985  *
19986  * Originally Released Under LGPL - original licence link has changed is not relivant.
19987  *
19988  * Fork - LGPL
19989  * <script type="text/javascript">
19990  */
19991
19992 /**
19993  * @class Roo.dd.DragSource
19994  * @extends Roo.dd.DDProxy
19995  * A simple class that provides the basic implementation needed to make any element draggable.
19996  * @constructor
19997  * @param {String/HTMLElement/Element} el The container element
19998  * @param {Object} config
19999  */
20000 Roo.dd.DragSource = function(el, config){
20001     this.el = Roo.get(el);
20002     this.dragData = {};
20003     
20004     Roo.apply(this, config);
20005     
20006     if(!this.proxy){
20007         this.proxy = new Roo.dd.StatusProxy();
20008     }
20009
20010     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20011           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20012     
20013     this.dragging = false;
20014 };
20015
20016 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20017     /**
20018      * @cfg {String} dropAllowed
20019      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20020      */
20021     dropAllowed : "x-dd-drop-ok",
20022     /**
20023      * @cfg {String} dropNotAllowed
20024      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20025      */
20026     dropNotAllowed : "x-dd-drop-nodrop",
20027
20028     /**
20029      * Returns the data object associated with this drag source
20030      * @return {Object} data An object containing arbitrary data
20031      */
20032     getDragData : function(e){
20033         return this.dragData;
20034     },
20035
20036     // private
20037     onDragEnter : function(e, id){
20038         var target = Roo.dd.DragDropMgr.getDDById(id);
20039         this.cachedTarget = target;
20040         if(this.beforeDragEnter(target, e, id) !== false){
20041             if(target.isNotifyTarget){
20042                 var status = target.notifyEnter(this, e, this.dragData);
20043                 this.proxy.setStatus(status);
20044             }else{
20045                 this.proxy.setStatus(this.dropAllowed);
20046             }
20047             
20048             if(this.afterDragEnter){
20049                 /**
20050                  * An empty function by default, but provided so that you can perform a custom action
20051                  * when the dragged item enters the drop target by providing an implementation.
20052                  * @param {Roo.dd.DragDrop} target The drop target
20053                  * @param {Event} e The event object
20054                  * @param {String} id The id of the dragged element
20055                  * @method afterDragEnter
20056                  */
20057                 this.afterDragEnter(target, e, id);
20058             }
20059         }
20060     },
20061
20062     /**
20063      * An empty function by default, but provided so that you can perform a custom action
20064      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20065      * @param {Roo.dd.DragDrop} target The drop target
20066      * @param {Event} e The event object
20067      * @param {String} id The id of the dragged element
20068      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20069      */
20070     beforeDragEnter : function(target, e, id){
20071         return true;
20072     },
20073
20074     // private
20075     alignElWithMouse: function() {
20076         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20077         this.proxy.sync();
20078     },
20079
20080     // private
20081     onDragOver : function(e, id){
20082         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20083         if(this.beforeDragOver(target, e, id) !== false){
20084             if(target.isNotifyTarget){
20085                 var status = target.notifyOver(this, e, this.dragData);
20086                 this.proxy.setStatus(status);
20087             }
20088
20089             if(this.afterDragOver){
20090                 /**
20091                  * An empty function by default, but provided so that you can perform a custom action
20092                  * while the dragged item is over the drop target by providing an implementation.
20093                  * @param {Roo.dd.DragDrop} target The drop target
20094                  * @param {Event} e The event object
20095                  * @param {String} id The id of the dragged element
20096                  * @method afterDragOver
20097                  */
20098                 this.afterDragOver(target, e, id);
20099             }
20100         }
20101     },
20102
20103     /**
20104      * An empty function by default, but provided so that you can perform a custom action
20105      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20106      * @param {Roo.dd.DragDrop} target The drop target
20107      * @param {Event} e The event object
20108      * @param {String} id The id of the dragged element
20109      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20110      */
20111     beforeDragOver : function(target, e, id){
20112         return true;
20113     },
20114
20115     // private
20116     onDragOut : function(e, id){
20117         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20118         if(this.beforeDragOut(target, e, id) !== false){
20119             if(target.isNotifyTarget){
20120                 target.notifyOut(this, e, this.dragData);
20121             }
20122             this.proxy.reset();
20123             if(this.afterDragOut){
20124                 /**
20125                  * An empty function by default, but provided so that you can perform a custom action
20126                  * after the dragged item is dragged out of the target without dropping.
20127                  * @param {Roo.dd.DragDrop} target The drop target
20128                  * @param {Event} e The event object
20129                  * @param {String} id The id of the dragged element
20130                  * @method afterDragOut
20131                  */
20132                 this.afterDragOut(target, e, id);
20133             }
20134         }
20135         this.cachedTarget = null;
20136     },
20137
20138     /**
20139      * An empty function by default, but provided so that you can perform a custom action before the dragged
20140      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20141      * @param {Roo.dd.DragDrop} target The drop target
20142      * @param {Event} e The event object
20143      * @param {String} id The id of the dragged element
20144      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20145      */
20146     beforeDragOut : function(target, e, id){
20147         return true;
20148     },
20149     
20150     // private
20151     onDragDrop : function(e, id){
20152         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20153         if(this.beforeDragDrop(target, e, id) !== false){
20154             if(target.isNotifyTarget){
20155                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20156                     this.onValidDrop(target, e, id);
20157                 }else{
20158                     this.onInvalidDrop(target, e, id);
20159                 }
20160             }else{
20161                 this.onValidDrop(target, e, id);
20162             }
20163             
20164             if(this.afterDragDrop){
20165                 /**
20166                  * An empty function by default, but provided so that you can perform a custom action
20167                  * after a valid drag drop has occurred by providing an implementation.
20168                  * @param {Roo.dd.DragDrop} target The drop target
20169                  * @param {Event} e The event object
20170                  * @param {String} id The id of the dropped element
20171                  * @method afterDragDrop
20172                  */
20173                 this.afterDragDrop(target, e, id);
20174             }
20175         }
20176         delete this.cachedTarget;
20177     },
20178
20179     /**
20180      * An empty function by default, but provided so that you can perform a custom action before the dragged
20181      * item is dropped onto the target and optionally cancel the onDragDrop.
20182      * @param {Roo.dd.DragDrop} target The drop target
20183      * @param {Event} e The event object
20184      * @param {String} id The id of the dragged element
20185      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20186      */
20187     beforeDragDrop : function(target, e, id){
20188         return true;
20189     },
20190
20191     // private
20192     onValidDrop : function(target, e, id){
20193         this.hideProxy();
20194         if(this.afterValidDrop){
20195             /**
20196              * An empty function by default, but provided so that you can perform a custom action
20197              * after a valid drop has occurred by providing an implementation.
20198              * @param {Object} target The target DD 
20199              * @param {Event} e The event object
20200              * @param {String} id The id of the dropped element
20201              * @method afterInvalidDrop
20202              */
20203             this.afterValidDrop(target, e, id);
20204         }
20205     },
20206
20207     // private
20208     getRepairXY : function(e, data){
20209         return this.el.getXY();  
20210     },
20211
20212     // private
20213     onInvalidDrop : function(target, e, id){
20214         this.beforeInvalidDrop(target, e, id);
20215         if(this.cachedTarget){
20216             if(this.cachedTarget.isNotifyTarget){
20217                 this.cachedTarget.notifyOut(this, e, this.dragData);
20218             }
20219             this.cacheTarget = null;
20220         }
20221         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20222
20223         if(this.afterInvalidDrop){
20224             /**
20225              * An empty function by default, but provided so that you can perform a custom action
20226              * after an invalid drop has occurred by providing an implementation.
20227              * @param {Event} e The event object
20228              * @param {String} id The id of the dropped element
20229              * @method afterInvalidDrop
20230              */
20231             this.afterInvalidDrop(e, id);
20232         }
20233     },
20234
20235     // private
20236     afterRepair : function(){
20237         if(Roo.enableFx){
20238             this.el.highlight(this.hlColor || "c3daf9");
20239         }
20240         this.dragging = false;
20241     },
20242
20243     /**
20244      * An empty function by default, but provided so that you can perform a custom action after an invalid
20245      * drop has occurred.
20246      * @param {Roo.dd.DragDrop} target The drop target
20247      * @param {Event} e The event object
20248      * @param {String} id The id of the dragged element
20249      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20250      */
20251     beforeInvalidDrop : function(target, e, id){
20252         return true;
20253     },
20254
20255     // private
20256     handleMouseDown : function(e){
20257         if(this.dragging) {
20258             return;
20259         }
20260         var data = this.getDragData(e);
20261         if(data && this.onBeforeDrag(data, e) !== false){
20262             this.dragData = data;
20263             this.proxy.stop();
20264             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20265         } 
20266     },
20267
20268     /**
20269      * An empty function by default, but provided so that you can perform a custom action before the initial
20270      * drag event begins and optionally cancel it.
20271      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20272      * @param {Event} e The event object
20273      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20274      */
20275     onBeforeDrag : function(data, e){
20276         return true;
20277     },
20278
20279     /**
20280      * An empty function by default, but provided so that you can perform a custom action once the initial
20281      * drag event has begun.  The drag cannot be canceled from this function.
20282      * @param {Number} x The x position of the click on the dragged object
20283      * @param {Number} y The y position of the click on the dragged object
20284      */
20285     onStartDrag : Roo.emptyFn,
20286
20287     // private - YUI override
20288     startDrag : function(x, y){
20289         this.proxy.reset();
20290         this.dragging = true;
20291         this.proxy.update("");
20292         this.onInitDrag(x, y);
20293         this.proxy.show();
20294     },
20295
20296     // private
20297     onInitDrag : function(x, y){
20298         var clone = this.el.dom.cloneNode(true);
20299         clone.id = Roo.id(); // prevent duplicate ids
20300         this.proxy.update(clone);
20301         this.onStartDrag(x, y);
20302         return true;
20303     },
20304
20305     /**
20306      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20307      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20308      */
20309     getProxy : function(){
20310         return this.proxy;  
20311     },
20312
20313     /**
20314      * Hides the drag source's {@link Roo.dd.StatusProxy}
20315      */
20316     hideProxy : function(){
20317         this.proxy.hide();  
20318         this.proxy.reset(true);
20319         this.dragging = false;
20320     },
20321
20322     // private
20323     triggerCacheRefresh : function(){
20324         Roo.dd.DDM.refreshCache(this.groups);
20325     },
20326
20327     // private - override to prevent hiding
20328     b4EndDrag: function(e) {
20329     },
20330
20331     // private - override to prevent moving
20332     endDrag : function(e){
20333         this.onEndDrag(this.dragData, e);
20334     },
20335
20336     // private
20337     onEndDrag : function(data, e){
20338     },
20339     
20340     // private - pin to cursor
20341     autoOffset : function(x, y) {
20342         this.setDelta(-12, -20);
20343     }    
20344 });/*
20345  * Based on:
20346  * Ext JS Library 1.1.1
20347  * Copyright(c) 2006-2007, Ext JS, LLC.
20348  *
20349  * Originally Released Under LGPL - original licence link has changed is not relivant.
20350  *
20351  * Fork - LGPL
20352  * <script type="text/javascript">
20353  */
20354
20355
20356 /**
20357  * @class Roo.dd.DropTarget
20358  * @extends Roo.dd.DDTarget
20359  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20360  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20361  * @constructor
20362  * @param {String/HTMLElement/Element} el The container element
20363  * @param {Object} config
20364  */
20365 Roo.dd.DropTarget = function(el, config){
20366     this.el = Roo.get(el);
20367     
20368     var listeners = false; ;
20369     if (config && config.listeners) {
20370         listeners= config.listeners;
20371         delete config.listeners;
20372     }
20373     Roo.apply(this, config);
20374     
20375     if(this.containerScroll){
20376         Roo.dd.ScrollManager.register(this.el);
20377     }
20378     this.addEvents( {
20379          /**
20380          * @scope Roo.dd.DropTarget
20381          */
20382          
20383          /**
20384          * @event enter
20385          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20386          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20387          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20388          * 
20389          * IMPORTANT : it should set this.overClass and this.dropAllowed
20390          * 
20391          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20392          * @param {Event} e The event
20393          * @param {Object} data An object containing arbitrary data supplied by the drag source
20394          */
20395         "enter" : true,
20396         
20397          /**
20398          * @event over
20399          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20400          * This method will be called on every mouse movement while the drag source is over the drop target.
20401          * This default implementation simply returns the dropAllowed config value.
20402          * 
20403          * IMPORTANT : it should set this.dropAllowed
20404          * 
20405          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20406          * @param {Event} e The event
20407          * @param {Object} data An object containing arbitrary data supplied by the drag source
20408          
20409          */
20410         "over" : true,
20411         /**
20412          * @event out
20413          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20414          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20415          * overClass (if any) from the drop element.
20416          * 
20417          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20418          * @param {Event} e The event
20419          * @param {Object} data An object containing arbitrary data supplied by the drag source
20420          */
20421          "out" : true,
20422          
20423         /**
20424          * @event drop
20425          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20426          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20427          * implementation that does something to process the drop event and returns true so that the drag source's
20428          * repair action does not run.
20429          * 
20430          * IMPORTANT : it should set this.success
20431          * 
20432          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20433          * @param {Event} e The event
20434          * @param {Object} data An object containing arbitrary data supplied by the drag source
20435         */
20436          "drop" : true
20437     });
20438             
20439      
20440     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20441         this.el.dom, 
20442         this.ddGroup || this.group,
20443         {
20444             isTarget: true,
20445             listeners : listeners || {} 
20446            
20447         
20448         }
20449     );
20450
20451 };
20452
20453 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20454     /**
20455      * @cfg {String} overClass
20456      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20457      */
20458      /**
20459      * @cfg {String} ddGroup
20460      * The drag drop group to handle drop events for
20461      */
20462      
20463     /**
20464      * @cfg {String} dropAllowed
20465      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20466      */
20467     dropAllowed : "x-dd-drop-ok",
20468     /**
20469      * @cfg {String} dropNotAllowed
20470      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20471      */
20472     dropNotAllowed : "x-dd-drop-nodrop",
20473     /**
20474      * @cfg {boolean} success
20475      * set this after drop listener.. 
20476      */
20477     success : false,
20478     /**
20479      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20480      * if the drop point is valid for over/enter..
20481      */
20482     valid : false,
20483     // private
20484     isTarget : true,
20485
20486     // private
20487     isNotifyTarget : true,
20488     
20489     /**
20490      * @hide
20491      */
20492     notifyEnter : function(dd, e, data)
20493     {
20494         this.valid = true;
20495         this.fireEvent('enter', dd, e, data);
20496         if(this.overClass){
20497             this.el.addClass(this.overClass);
20498         }
20499         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20500             this.valid ? this.dropAllowed : this.dropNotAllowed
20501         );
20502     },
20503
20504     /**
20505      * @hide
20506      */
20507     notifyOver : function(dd, e, data)
20508     {
20509         this.valid = true;
20510         this.fireEvent('over', dd, e, data);
20511         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20512             this.valid ? this.dropAllowed : this.dropNotAllowed
20513         );
20514     },
20515
20516     /**
20517      * @hide
20518      */
20519     notifyOut : function(dd, e, data)
20520     {
20521         this.fireEvent('out', dd, e, data);
20522         if(this.overClass){
20523             this.el.removeClass(this.overClass);
20524         }
20525     },
20526
20527     /**
20528      * @hide
20529      */
20530     notifyDrop : function(dd, e, data)
20531     {
20532         this.success = false;
20533         this.fireEvent('drop', dd, e, data);
20534         return this.success;
20535     }
20536 });/*
20537  * Based on:
20538  * Ext JS Library 1.1.1
20539  * Copyright(c) 2006-2007, Ext JS, LLC.
20540  *
20541  * Originally Released Under LGPL - original licence link has changed is not relivant.
20542  *
20543  * Fork - LGPL
20544  * <script type="text/javascript">
20545  */
20546
20547
20548 /**
20549  * @class Roo.dd.DragZone
20550  * @extends Roo.dd.DragSource
20551  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20552  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20553  * @constructor
20554  * @param {String/HTMLElement/Element} el The container element
20555  * @param {Object} config
20556  */
20557 Roo.dd.DragZone = function(el, config){
20558     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20559     if(this.containerScroll){
20560         Roo.dd.ScrollManager.register(this.el);
20561     }
20562 };
20563
20564 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20565     /**
20566      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20567      * for auto scrolling during drag operations.
20568      */
20569     /**
20570      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20571      * method after a failed drop (defaults to "c3daf9" - light blue)
20572      */
20573
20574     /**
20575      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20576      * for a valid target to drag based on the mouse down. Override this method
20577      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20578      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20579      * @param {EventObject} e The mouse down event
20580      * @return {Object} The dragData
20581      */
20582     getDragData : function(e){
20583         return Roo.dd.Registry.getHandleFromEvent(e);
20584     },
20585     
20586     /**
20587      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20588      * this.dragData.ddel
20589      * @param {Number} x The x position of the click on the dragged object
20590      * @param {Number} y The y position of the click on the dragged object
20591      * @return {Boolean} true to continue the drag, false to cancel
20592      */
20593     onInitDrag : function(x, y){
20594         this.proxy.update(this.dragData.ddel.cloneNode(true));
20595         this.onStartDrag(x, y);
20596         return true;
20597     },
20598     
20599     /**
20600      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20601      */
20602     afterRepair : function(){
20603         if(Roo.enableFx){
20604             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20605         }
20606         this.dragging = false;
20607     },
20608
20609     /**
20610      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20611      * the XY of this.dragData.ddel
20612      * @param {EventObject} e The mouse up event
20613      * @return {Array} The xy location (e.g. [100, 200])
20614      */
20615     getRepairXY : function(e){
20616         return Roo.Element.fly(this.dragData.ddel).getXY();  
20617     }
20618 });/*
20619  * Based on:
20620  * Ext JS Library 1.1.1
20621  * Copyright(c) 2006-2007, Ext JS, LLC.
20622  *
20623  * Originally Released Under LGPL - original licence link has changed is not relivant.
20624  *
20625  * Fork - LGPL
20626  * <script type="text/javascript">
20627  */
20628 /**
20629  * @class Roo.dd.DropZone
20630  * @extends Roo.dd.DropTarget
20631  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20632  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20633  * @constructor
20634  * @param {String/HTMLElement/Element} el The container element
20635  * @param {Object} config
20636  */
20637 Roo.dd.DropZone = function(el, config){
20638     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20639 };
20640
20641 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20642     /**
20643      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20644      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20645      * provide your own custom lookup.
20646      * @param {Event} e The event
20647      * @return {Object} data The custom data
20648      */
20649     getTargetFromEvent : function(e){
20650         return Roo.dd.Registry.getTargetFromEvent(e);
20651     },
20652
20653     /**
20654      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20655      * that it has registered.  This method has no default implementation and should be overridden to provide
20656      * node-specific processing if necessary.
20657      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20658      * {@link #getTargetFromEvent} for this node)
20659      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20660      * @param {Event} e The event
20661      * @param {Object} data An object containing arbitrary data supplied by the drag source
20662      */
20663     onNodeEnter : function(n, dd, e, data){
20664         
20665     },
20666
20667     /**
20668      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20669      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20670      * overridden to provide the proper feedback.
20671      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20672      * {@link #getTargetFromEvent} for this node)
20673      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20674      * @param {Event} e The event
20675      * @param {Object} data An object containing arbitrary data supplied by the drag source
20676      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20677      * underlying {@link Roo.dd.StatusProxy} can be updated
20678      */
20679     onNodeOver : function(n, dd, e, data){
20680         return this.dropAllowed;
20681     },
20682
20683     /**
20684      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20685      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20686      * node-specific processing if necessary.
20687      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20688      * {@link #getTargetFromEvent} for this node)
20689      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20690      * @param {Event} e The event
20691      * @param {Object} data An object containing arbitrary data supplied by the drag source
20692      */
20693     onNodeOut : function(n, dd, e, data){
20694         
20695     },
20696
20697     /**
20698      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20699      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20700      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20701      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20702      * {@link #getTargetFromEvent} for this node)
20703      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20704      * @param {Event} e The event
20705      * @param {Object} data An object containing arbitrary data supplied by the drag source
20706      * @return {Boolean} True if the drop was valid, else false
20707      */
20708     onNodeDrop : function(n, dd, e, data){
20709         return false;
20710     },
20711
20712     /**
20713      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20714      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20715      * it should be overridden to provide the proper feedback if necessary.
20716      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20717      * @param {Event} e The event
20718      * @param {Object} data An object containing arbitrary data supplied by the drag source
20719      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20720      * underlying {@link Roo.dd.StatusProxy} can be updated
20721      */
20722     onContainerOver : function(dd, e, data){
20723         return this.dropNotAllowed;
20724     },
20725
20726     /**
20727      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20728      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20729      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20730      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20731      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20732      * @param {Event} e The event
20733      * @param {Object} data An object containing arbitrary data supplied by the drag source
20734      * @return {Boolean} True if the drop was valid, else false
20735      */
20736     onContainerDrop : function(dd, e, data){
20737         return false;
20738     },
20739
20740     /**
20741      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20742      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20743      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20744      * you should override this method and provide a custom implementation.
20745      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20746      * @param {Event} e The event
20747      * @param {Object} data An object containing arbitrary data supplied by the drag source
20748      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20749      * underlying {@link Roo.dd.StatusProxy} can be updated
20750      */
20751     notifyEnter : function(dd, e, data){
20752         return this.dropNotAllowed;
20753     },
20754
20755     /**
20756      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20757      * This method will be called on every mouse movement while the drag source is over the drop zone.
20758      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20759      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20760      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20761      * registered node, it will call {@link #onContainerOver}.
20762      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20763      * @param {Event} e The event
20764      * @param {Object} data An object containing arbitrary data supplied by the drag source
20765      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20766      * underlying {@link Roo.dd.StatusProxy} can be updated
20767      */
20768     notifyOver : function(dd, e, data){
20769         var n = this.getTargetFromEvent(e);
20770         if(!n){ // not over valid drop target
20771             if(this.lastOverNode){
20772                 this.onNodeOut(this.lastOverNode, dd, e, data);
20773                 this.lastOverNode = null;
20774             }
20775             return this.onContainerOver(dd, e, data);
20776         }
20777         if(this.lastOverNode != n){
20778             if(this.lastOverNode){
20779                 this.onNodeOut(this.lastOverNode, dd, e, data);
20780             }
20781             this.onNodeEnter(n, dd, e, data);
20782             this.lastOverNode = n;
20783         }
20784         return this.onNodeOver(n, dd, e, data);
20785     },
20786
20787     /**
20788      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20789      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20790      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20791      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20792      * @param {Event} e The event
20793      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20794      */
20795     notifyOut : function(dd, e, data){
20796         if(this.lastOverNode){
20797             this.onNodeOut(this.lastOverNode, dd, e, data);
20798             this.lastOverNode = null;
20799         }
20800     },
20801
20802     /**
20803      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20804      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20805      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20806      * otherwise it will call {@link #onContainerDrop}.
20807      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20808      * @param {Event} e The event
20809      * @param {Object} data An object containing arbitrary data supplied by the drag source
20810      * @return {Boolean} True if the drop was valid, else false
20811      */
20812     notifyDrop : function(dd, e, data){
20813         if(this.lastOverNode){
20814             this.onNodeOut(this.lastOverNode, dd, e, data);
20815             this.lastOverNode = null;
20816         }
20817         var n = this.getTargetFromEvent(e);
20818         return n ?
20819             this.onNodeDrop(n, dd, e, data) :
20820             this.onContainerDrop(dd, e, data);
20821     },
20822
20823     // private
20824     triggerCacheRefresh : function(){
20825         Roo.dd.DDM.refreshCache(this.groups);
20826     }  
20827 });/*
20828  * Based on:
20829  * Ext JS Library 1.1.1
20830  * Copyright(c) 2006-2007, Ext JS, LLC.
20831  *
20832  * Originally Released Under LGPL - original licence link has changed is not relivant.
20833  *
20834  * Fork - LGPL
20835  * <script type="text/javascript">
20836  */
20837
20838
20839 /**
20840  * @class Roo.data.SortTypes
20841  * @singleton
20842  * Defines the default sorting (casting?) comparison functions used when sorting data.
20843  */
20844 Roo.data.SortTypes = {
20845     /**
20846      * Default sort that does nothing
20847      * @param {Mixed} s The value being converted
20848      * @return {Mixed} The comparison value
20849      */
20850     none : function(s){
20851         return s;
20852     },
20853     
20854     /**
20855      * The regular expression used to strip tags
20856      * @type {RegExp}
20857      * @property
20858      */
20859     stripTagsRE : /<\/?[^>]+>/gi,
20860     
20861     /**
20862      * Strips all HTML tags to sort on text only
20863      * @param {Mixed} s The value being converted
20864      * @return {String} The comparison value
20865      */
20866     asText : function(s){
20867         return String(s).replace(this.stripTagsRE, "");
20868     },
20869     
20870     /**
20871      * Strips all HTML tags to sort on text only - Case insensitive
20872      * @param {Mixed} s The value being converted
20873      * @return {String} The comparison value
20874      */
20875     asUCText : function(s){
20876         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20877     },
20878     
20879     /**
20880      * Case insensitive string
20881      * @param {Mixed} s The value being converted
20882      * @return {String} The comparison value
20883      */
20884     asUCString : function(s) {
20885         return String(s).toUpperCase();
20886     },
20887     
20888     /**
20889      * Date sorting
20890      * @param {Mixed} s The value being converted
20891      * @return {Number} The comparison value
20892      */
20893     asDate : function(s) {
20894         if(!s){
20895             return 0;
20896         }
20897         if(s instanceof Date){
20898             return s.getTime();
20899         }
20900         return Date.parse(String(s));
20901     },
20902     
20903     /**
20904      * Float sorting
20905      * @param {Mixed} s The value being converted
20906      * @return {Float} The comparison value
20907      */
20908     asFloat : function(s) {
20909         var val = parseFloat(String(s).replace(/,/g, ""));
20910         if(isNaN(val)) val = 0;
20911         return val;
20912     },
20913     
20914     /**
20915      * Integer sorting
20916      * @param {Mixed} s The value being converted
20917      * @return {Number} The comparison value
20918      */
20919     asInt : function(s) {
20920         var val = parseInt(String(s).replace(/,/g, ""));
20921         if(isNaN(val)) val = 0;
20922         return val;
20923     }
20924 };/*
20925  * Based on:
20926  * Ext JS Library 1.1.1
20927  * Copyright(c) 2006-2007, Ext JS, LLC.
20928  *
20929  * Originally Released Under LGPL - original licence link has changed is not relivant.
20930  *
20931  * Fork - LGPL
20932  * <script type="text/javascript">
20933  */
20934
20935 /**
20936 * @class Roo.data.Record
20937  * Instances of this class encapsulate both record <em>definition</em> information, and record
20938  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20939  * to access Records cached in an {@link Roo.data.Store} object.<br>
20940  * <p>
20941  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20942  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20943  * objects.<br>
20944  * <p>
20945  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20946  * @constructor
20947  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20948  * {@link #create}. The parameters are the same.
20949  * @param {Array} data An associative Array of data values keyed by the field name.
20950  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20951  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20952  * not specified an integer id is generated.
20953  */
20954 Roo.data.Record = function(data, id){
20955     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20956     this.data = data;
20957 };
20958
20959 /**
20960  * Generate a constructor for a specific record layout.
20961  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20962  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20963  * Each field definition object may contain the following properties: <ul>
20964  * <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,
20965  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20966  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20967  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20968  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20969  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20970  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20971  * this may be omitted.</p></li>
20972  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20973  * <ul><li>auto (Default, implies no conversion)</li>
20974  * <li>string</li>
20975  * <li>int</li>
20976  * <li>float</li>
20977  * <li>boolean</li>
20978  * <li>date</li></ul></p></li>
20979  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20980  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20981  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20982  * by the Reader into an object that will be stored in the Record. It is passed the
20983  * following parameters:<ul>
20984  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20985  * </ul></p></li>
20986  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20987  * </ul>
20988  * <br>usage:<br><pre><code>
20989 var TopicRecord = Roo.data.Record.create(
20990     {name: 'title', mapping: 'topic_title'},
20991     {name: 'author', mapping: 'username'},
20992     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20993     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20994     {name: 'lastPoster', mapping: 'user2'},
20995     {name: 'excerpt', mapping: 'post_text'}
20996 );
20997
20998 var myNewRecord = new TopicRecord({
20999     title: 'Do my job please',
21000     author: 'noobie',
21001     totalPosts: 1,
21002     lastPost: new Date(),
21003     lastPoster: 'Animal',
21004     excerpt: 'No way dude!'
21005 });
21006 myStore.add(myNewRecord);
21007 </code></pre>
21008  * @method create
21009  * @static
21010  */
21011 Roo.data.Record.create = function(o){
21012     var f = function(){
21013         f.superclass.constructor.apply(this, arguments);
21014     };
21015     Roo.extend(f, Roo.data.Record);
21016     var p = f.prototype;
21017     p.fields = new Roo.util.MixedCollection(false, function(field){
21018         return field.name;
21019     });
21020     for(var i = 0, len = o.length; i < len; i++){
21021         p.fields.add(new Roo.data.Field(o[i]));
21022     }
21023     f.getField = function(name){
21024         return p.fields.get(name);  
21025     };
21026     return f;
21027 };
21028
21029 Roo.data.Record.AUTO_ID = 1000;
21030 Roo.data.Record.EDIT = 'edit';
21031 Roo.data.Record.REJECT = 'reject';
21032 Roo.data.Record.COMMIT = 'commit';
21033
21034 Roo.data.Record.prototype = {
21035     /**
21036      * Readonly flag - true if this record has been modified.
21037      * @type Boolean
21038      */
21039     dirty : false,
21040     editing : false,
21041     error: null,
21042     modified: null,
21043
21044     // private
21045     join : function(store){
21046         this.store = store;
21047     },
21048
21049     /**
21050      * Set the named field to the specified value.
21051      * @param {String} name The name of the field to set.
21052      * @param {Object} value The value to set the field to.
21053      */
21054     set : function(name, value){
21055         if(this.data[name] == value){
21056             return;
21057         }
21058         this.dirty = true;
21059         if(!this.modified){
21060             this.modified = {};
21061         }
21062         if(typeof this.modified[name] == 'undefined'){
21063             this.modified[name] = this.data[name];
21064         }
21065         this.data[name] = value;
21066         if(!this.editing && this.store){
21067             this.store.afterEdit(this);
21068         }       
21069     },
21070
21071     /**
21072      * Get the value of the named field.
21073      * @param {String} name The name of the field to get the value of.
21074      * @return {Object} The value of the field.
21075      */
21076     get : function(name){
21077         return this.data[name]; 
21078     },
21079
21080     // private
21081     beginEdit : function(){
21082         this.editing = true;
21083         this.modified = {}; 
21084     },
21085
21086     // private
21087     cancelEdit : function(){
21088         this.editing = false;
21089         delete this.modified;
21090     },
21091
21092     // private
21093     endEdit : function(){
21094         this.editing = false;
21095         if(this.dirty && this.store){
21096             this.store.afterEdit(this);
21097         }
21098     },
21099
21100     /**
21101      * Usually called by the {@link Roo.data.Store} which owns the Record.
21102      * Rejects all changes made to the Record since either creation, or the last commit operation.
21103      * Modified fields are reverted to their original values.
21104      * <p>
21105      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21106      * of reject operations.
21107      */
21108     reject : function(){
21109         var m = this.modified;
21110         for(var n in m){
21111             if(typeof m[n] != "function"){
21112                 this.data[n] = m[n];
21113             }
21114         }
21115         this.dirty = false;
21116         delete this.modified;
21117         this.editing = false;
21118         if(this.store){
21119             this.store.afterReject(this);
21120         }
21121     },
21122
21123     /**
21124      * Usually called by the {@link Roo.data.Store} which owns the Record.
21125      * Commits all changes made to the Record since either creation, or the last commit operation.
21126      * <p>
21127      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21128      * of commit operations.
21129      */
21130     commit : function(){
21131         this.dirty = false;
21132         delete this.modified;
21133         this.editing = false;
21134         if(this.store){
21135             this.store.afterCommit(this);
21136         }
21137     },
21138
21139     // private
21140     hasError : function(){
21141         return this.error != null;
21142     },
21143
21144     // private
21145     clearError : function(){
21146         this.error = null;
21147     },
21148
21149     /**
21150      * Creates a copy of this record.
21151      * @param {String} id (optional) A new record id if you don't want to use this record's id
21152      * @return {Record}
21153      */
21154     copy : function(newId) {
21155         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21156     }
21157 };/*
21158  * Based on:
21159  * Ext JS Library 1.1.1
21160  * Copyright(c) 2006-2007, Ext JS, LLC.
21161  *
21162  * Originally Released Under LGPL - original licence link has changed is not relivant.
21163  *
21164  * Fork - LGPL
21165  * <script type="text/javascript">
21166  */
21167
21168
21169
21170 /**
21171  * @class Roo.data.Store
21172  * @extends Roo.util.Observable
21173  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21174  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21175  * <p>
21176  * 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
21177  * has no knowledge of the format of the data returned by the Proxy.<br>
21178  * <p>
21179  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21180  * instances from the data object. These records are cached and made available through accessor functions.
21181  * @constructor
21182  * Creates a new Store.
21183  * @param {Object} config A config object containing the objects needed for the Store to access data,
21184  * and read the data into Records.
21185  */
21186 Roo.data.Store = function(config){
21187     this.data = new Roo.util.MixedCollection(false);
21188     this.data.getKey = function(o){
21189         return o.id;
21190     };
21191     this.baseParams = {};
21192     // private
21193     this.paramNames = {
21194         "start" : "start",
21195         "limit" : "limit",
21196         "sort" : "sort",
21197         "dir" : "dir",
21198         "multisort" : "_multisort"
21199     };
21200
21201     if(config && config.data){
21202         this.inlineData = config.data;
21203         delete config.data;
21204     }
21205
21206     Roo.apply(this, config);
21207     
21208     if(this.reader){ // reader passed
21209         this.reader = Roo.factory(this.reader, Roo.data);
21210         this.reader.xmodule = this.xmodule || false;
21211         if(!this.recordType){
21212             this.recordType = this.reader.recordType;
21213         }
21214         if(this.reader.onMetaChange){
21215             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21216         }
21217     }
21218
21219     if(this.recordType){
21220         this.fields = this.recordType.prototype.fields;
21221     }
21222     this.modified = [];
21223
21224     this.addEvents({
21225         /**
21226          * @event datachanged
21227          * Fires when the data cache has changed, and a widget which is using this Store
21228          * as a Record cache should refresh its view.
21229          * @param {Store} this
21230          */
21231         datachanged : true,
21232         /**
21233          * @event metachange
21234          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21235          * @param {Store} this
21236          * @param {Object} meta The JSON metadata
21237          */
21238         metachange : true,
21239         /**
21240          * @event add
21241          * Fires when Records have been added to the Store
21242          * @param {Store} this
21243          * @param {Roo.data.Record[]} records The array of Records added
21244          * @param {Number} index The index at which the record(s) were added
21245          */
21246         add : true,
21247         /**
21248          * @event remove
21249          * Fires when a Record has been removed from the Store
21250          * @param {Store} this
21251          * @param {Roo.data.Record} record The Record that was removed
21252          * @param {Number} index The index at which the record was removed
21253          */
21254         remove : true,
21255         /**
21256          * @event update
21257          * Fires when a Record has been updated
21258          * @param {Store} this
21259          * @param {Roo.data.Record} record The Record that was updated
21260          * @param {String} operation The update operation being performed.  Value may be one of:
21261          * <pre><code>
21262  Roo.data.Record.EDIT
21263  Roo.data.Record.REJECT
21264  Roo.data.Record.COMMIT
21265          * </code></pre>
21266          */
21267         update : true,
21268         /**
21269          * @event clear
21270          * Fires when the data cache has been cleared.
21271          * @param {Store} this
21272          */
21273         clear : true,
21274         /**
21275          * @event beforeload
21276          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21277          * the load action will be canceled.
21278          * @param {Store} this
21279          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21280          */
21281         beforeload : true,
21282         /**
21283          * @event beforeloadadd
21284          * Fires after a new set of Records has been loaded.
21285          * @param {Store} this
21286          * @param {Roo.data.Record[]} records The Records that were loaded
21287          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21288          */
21289         beforeloadadd : true,
21290         /**
21291          * @event load
21292          * Fires after a new set of Records has been loaded, before they are added to the store.
21293          * @param {Store} this
21294          * @param {Roo.data.Record[]} records The Records that were loaded
21295          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21296          * @params {Object} return from reader
21297          */
21298         load : true,
21299         /**
21300          * @event loadexception
21301          * Fires if an exception occurs in the Proxy during loading.
21302          * Called with the signature of the Proxy's "loadexception" event.
21303          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21304          * 
21305          * @param {Proxy} 
21306          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21307          * @param {Object} load options 
21308          * @param {Object} jsonData from your request (normally this contains the Exception)
21309          */
21310         loadexception : true
21311     });
21312     
21313     if(this.proxy){
21314         this.proxy = Roo.factory(this.proxy, Roo.data);
21315         this.proxy.xmodule = this.xmodule || false;
21316         this.relayEvents(this.proxy,  ["loadexception"]);
21317     }
21318     this.sortToggle = {};
21319     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21320
21321     Roo.data.Store.superclass.constructor.call(this);
21322
21323     if(this.inlineData){
21324         this.loadData(this.inlineData);
21325         delete this.inlineData;
21326     }
21327 };
21328
21329 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21330      /**
21331     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21332     * without a remote query - used by combo/forms at present.
21333     */
21334     
21335     /**
21336     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21337     */
21338     /**
21339     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21340     */
21341     /**
21342     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21343     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21344     */
21345     /**
21346     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21347     * on any HTTP request
21348     */
21349     /**
21350     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21351     */
21352     /**
21353     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21354     */
21355     multiSort: false,
21356     /**
21357     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21358     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21359     */
21360     remoteSort : false,
21361
21362     /**
21363     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21364      * loaded or when a record is removed. (defaults to false).
21365     */
21366     pruneModifiedRecords : false,
21367
21368     // private
21369     lastOptions : null,
21370
21371     /**
21372      * Add Records to the Store and fires the add event.
21373      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21374      */
21375     add : function(records){
21376         records = [].concat(records);
21377         for(var i = 0, len = records.length; i < len; i++){
21378             records[i].join(this);
21379         }
21380         var index = this.data.length;
21381         this.data.addAll(records);
21382         this.fireEvent("add", this, records, index);
21383     },
21384
21385     /**
21386      * Remove a Record from the Store and fires the remove event.
21387      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21388      */
21389     remove : function(record){
21390         var index = this.data.indexOf(record);
21391         this.data.removeAt(index);
21392         if(this.pruneModifiedRecords){
21393             this.modified.remove(record);
21394         }
21395         this.fireEvent("remove", this, record, index);
21396     },
21397
21398     /**
21399      * Remove all Records from the Store and fires the clear event.
21400      */
21401     removeAll : function(){
21402         this.data.clear();
21403         if(this.pruneModifiedRecords){
21404             this.modified = [];
21405         }
21406         this.fireEvent("clear", this);
21407     },
21408
21409     /**
21410      * Inserts Records to the Store at the given index and fires the add event.
21411      * @param {Number} index The start index at which to insert the passed Records.
21412      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21413      */
21414     insert : function(index, records){
21415         records = [].concat(records);
21416         for(var i = 0, len = records.length; i < len; i++){
21417             this.data.insert(index, records[i]);
21418             records[i].join(this);
21419         }
21420         this.fireEvent("add", this, records, index);
21421     },
21422
21423     /**
21424      * Get the index within the cache of the passed Record.
21425      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21426      * @return {Number} The index of the passed Record. Returns -1 if not found.
21427      */
21428     indexOf : function(record){
21429         return this.data.indexOf(record);
21430     },
21431
21432     /**
21433      * Get the index within the cache of the Record with the passed id.
21434      * @param {String} id The id of the Record to find.
21435      * @return {Number} The index of the Record. Returns -1 if not found.
21436      */
21437     indexOfId : function(id){
21438         return this.data.indexOfKey(id);
21439     },
21440
21441     /**
21442      * Get the Record with the specified id.
21443      * @param {String} id The id of the Record to find.
21444      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21445      */
21446     getById : function(id){
21447         return this.data.key(id);
21448     },
21449
21450     /**
21451      * Get the Record at the specified index.
21452      * @param {Number} index The index of the Record to find.
21453      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21454      */
21455     getAt : function(index){
21456         return this.data.itemAt(index);
21457     },
21458
21459     /**
21460      * Returns a range of Records between specified indices.
21461      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21462      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21463      * @return {Roo.data.Record[]} An array of Records
21464      */
21465     getRange : function(start, end){
21466         return this.data.getRange(start, end);
21467     },
21468
21469     // private
21470     storeOptions : function(o){
21471         o = Roo.apply({}, o);
21472         delete o.callback;
21473         delete o.scope;
21474         this.lastOptions = o;
21475     },
21476
21477     /**
21478      * Loads the Record cache from the configured Proxy using the configured Reader.
21479      * <p>
21480      * If using remote paging, then the first load call must specify the <em>start</em>
21481      * and <em>limit</em> properties in the options.params property to establish the initial
21482      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21483      * <p>
21484      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21485      * and this call will return before the new data has been loaded. Perform any post-processing
21486      * in a callback function, or in a "load" event handler.</strong>
21487      * <p>
21488      * @param {Object} options An object containing properties which control loading options:<ul>
21489      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21490      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21491      * passed the following arguments:<ul>
21492      * <li>r : Roo.data.Record[]</li>
21493      * <li>options: Options object from the load call</li>
21494      * <li>success: Boolean success indicator</li></ul></li>
21495      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21496      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21497      * </ul>
21498      */
21499     load : function(options){
21500         options = options || {};
21501         if(this.fireEvent("beforeload", this, options) !== false){
21502             this.storeOptions(options);
21503             var p = Roo.apply(options.params || {}, this.baseParams);
21504             // if meta was not loaded from remote source.. try requesting it.
21505             if (!this.reader.metaFromRemote) {
21506                 p._requestMeta = 1;
21507             }
21508             if(this.sortInfo && this.remoteSort){
21509                 var pn = this.paramNames;
21510                 p[pn["sort"]] = this.sortInfo.field;
21511                 p[pn["dir"]] = this.sortInfo.direction;
21512             }
21513             if (this.multiSort) {
21514                 var pn = this.paramNames;
21515                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21516             }
21517             
21518             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21519         }
21520     },
21521
21522     /**
21523      * Reloads the Record cache from the configured Proxy using the configured Reader and
21524      * the options from the last load operation performed.
21525      * @param {Object} options (optional) An object containing properties which may override the options
21526      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21527      * the most recently used options are reused).
21528      */
21529     reload : function(options){
21530         this.load(Roo.applyIf(options||{}, this.lastOptions));
21531     },
21532
21533     // private
21534     // Called as a callback by the Reader during a load operation.
21535     loadRecords : function(o, options, success){
21536         if(!o || success === false){
21537             if(success !== false){
21538                 this.fireEvent("load", this, [], options, o);
21539             }
21540             if(options.callback){
21541                 options.callback.call(options.scope || this, [], options, false);
21542             }
21543             return;
21544         }
21545         // if data returned failure - throw an exception.
21546         if (o.success === false) {
21547             // show a message if no listener is registered.
21548             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21549                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21550             }
21551             // loadmask wil be hooked into this..
21552             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21553             return;
21554         }
21555         var r = o.records, t = o.totalRecords || r.length;
21556         
21557         this.fireEvent("beforeloadadd", this, r, options, o);
21558         
21559         if(!options || options.add !== true){
21560             if(this.pruneModifiedRecords){
21561                 this.modified = [];
21562             }
21563             for(var i = 0, len = r.length; i < len; i++){
21564                 r[i].join(this);
21565             }
21566             if(this.snapshot){
21567                 this.data = this.snapshot;
21568                 delete this.snapshot;
21569             }
21570             this.data.clear();
21571             this.data.addAll(r);
21572             this.totalLength = t;
21573             this.applySort();
21574             this.fireEvent("datachanged", this);
21575         }else{
21576             this.totalLength = Math.max(t, this.data.length+r.length);
21577             this.add(r);
21578         }
21579         this.fireEvent("load", this, r, options, o);
21580         if(options.callback){
21581             options.callback.call(options.scope || this, r, options, true);
21582         }
21583     },
21584
21585
21586     /**
21587      * Loads data from a passed data block. A Reader which understands the format of the data
21588      * must have been configured in the constructor.
21589      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21590      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21591      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21592      */
21593     loadData : function(o, append){
21594         var r = this.reader.readRecords(o);
21595         this.loadRecords(r, {add: append}, true);
21596     },
21597
21598     /**
21599      * Gets the number of cached records.
21600      * <p>
21601      * <em>If using paging, this may not be the total size of the dataset. If the data object
21602      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21603      * the data set size</em>
21604      */
21605     getCount : function(){
21606         return this.data.length || 0;
21607     },
21608
21609     /**
21610      * Gets the total number of records in the dataset as returned by the server.
21611      * <p>
21612      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21613      * the dataset size</em>
21614      */
21615     getTotalCount : function(){
21616         return this.totalLength || 0;
21617     },
21618
21619     /**
21620      * Returns the sort state of the Store as an object with two properties:
21621      * <pre><code>
21622  field {String} The name of the field by which the Records are sorted
21623  direction {String} The sort order, "ASC" or "DESC"
21624      * </code></pre>
21625      */
21626     getSortState : function(){
21627         return this.sortInfo;
21628     },
21629
21630     // private
21631     applySort : function(){
21632         if(this.sortInfo && !this.remoteSort){
21633             var s = this.sortInfo, f = s.field;
21634             var st = this.fields.get(f).sortType;
21635             var fn = function(r1, r2){
21636                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21637                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21638             };
21639             this.data.sort(s.direction, fn);
21640             if(this.snapshot && this.snapshot != this.data){
21641                 this.snapshot.sort(s.direction, fn);
21642             }
21643         }
21644     },
21645
21646     /**
21647      * Sets the default sort column and order to be used by the next load operation.
21648      * @param {String} fieldName The name of the field to sort by.
21649      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21650      */
21651     setDefaultSort : function(field, dir){
21652         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21653     },
21654
21655     /**
21656      * Sort the Records.
21657      * If remote sorting is used, the sort is performed on the server, and the cache is
21658      * reloaded. If local sorting is used, the cache is sorted internally.
21659      * @param {String} fieldName The name of the field to sort by.
21660      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21661      */
21662     sort : function(fieldName, dir){
21663         var f = this.fields.get(fieldName);
21664         if(!dir){
21665             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21666             
21667             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21668                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21669             }else{
21670                 dir = f.sortDir;
21671             }
21672         }
21673         this.sortToggle[f.name] = dir;
21674         this.sortInfo = {field: f.name, direction: dir};
21675         if(!this.remoteSort){
21676             this.applySort();
21677             this.fireEvent("datachanged", this);
21678         }else{
21679             this.load(this.lastOptions);
21680         }
21681     },
21682
21683     /**
21684      * Calls the specified function for each of the Records in the cache.
21685      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21686      * Returning <em>false</em> aborts and exits the iteration.
21687      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21688      */
21689     each : function(fn, scope){
21690         this.data.each(fn, scope);
21691     },
21692
21693     /**
21694      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21695      * (e.g., during paging).
21696      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21697      */
21698     getModifiedRecords : function(){
21699         return this.modified;
21700     },
21701
21702     // private
21703     createFilterFn : function(property, value, anyMatch){
21704         if(!value.exec){ // not a regex
21705             value = String(value);
21706             if(value.length == 0){
21707                 return false;
21708             }
21709             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21710         }
21711         return function(r){
21712             return value.test(r.data[property]);
21713         };
21714     },
21715
21716     /**
21717      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21718      * @param {String} property A field on your records
21719      * @param {Number} start The record index to start at (defaults to 0)
21720      * @param {Number} end The last record index to include (defaults to length - 1)
21721      * @return {Number} The sum
21722      */
21723     sum : function(property, start, end){
21724         var rs = this.data.items, v = 0;
21725         start = start || 0;
21726         end = (end || end === 0) ? end : rs.length-1;
21727
21728         for(var i = start; i <= end; i++){
21729             v += (rs[i].data[property] || 0);
21730         }
21731         return v;
21732     },
21733
21734     /**
21735      * Filter the records by a specified property.
21736      * @param {String} field A field on your records
21737      * @param {String/RegExp} value Either a string that the field
21738      * should start with or a RegExp to test against the field
21739      * @param {Boolean} anyMatch True to match any part not just the beginning
21740      */
21741     filter : function(property, value, anyMatch){
21742         var fn = this.createFilterFn(property, value, anyMatch);
21743         return fn ? this.filterBy(fn) : this.clearFilter();
21744     },
21745
21746     /**
21747      * Filter by a function. The specified function will be called with each
21748      * record in this data source. If the function returns true the record is included,
21749      * otherwise it is filtered.
21750      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21751      * @param {Object} scope (optional) The scope of the function (defaults to this)
21752      */
21753     filterBy : function(fn, scope){
21754         this.snapshot = this.snapshot || this.data;
21755         this.data = this.queryBy(fn, scope||this);
21756         this.fireEvent("datachanged", this);
21757     },
21758
21759     /**
21760      * Query the records by a specified property.
21761      * @param {String} field A field on your records
21762      * @param {String/RegExp} value Either a string that the field
21763      * should start with or a RegExp to test against the field
21764      * @param {Boolean} anyMatch True to match any part not just the beginning
21765      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21766      */
21767     query : function(property, value, anyMatch){
21768         var fn = this.createFilterFn(property, value, anyMatch);
21769         return fn ? this.queryBy(fn) : this.data.clone();
21770     },
21771
21772     /**
21773      * Query by a function. The specified function will be called with each
21774      * record in this data source. If the function returns true the record is included
21775      * in the results.
21776      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21777      * @param {Object} scope (optional) The scope of the function (defaults to this)
21778       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21779      **/
21780     queryBy : function(fn, scope){
21781         var data = this.snapshot || this.data;
21782         return data.filterBy(fn, scope||this);
21783     },
21784
21785     /**
21786      * Collects unique values for a particular dataIndex from this store.
21787      * @param {String} dataIndex The property to collect
21788      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21789      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21790      * @return {Array} An array of the unique values
21791      **/
21792     collect : function(dataIndex, allowNull, bypassFilter){
21793         var d = (bypassFilter === true && this.snapshot) ?
21794                 this.snapshot.items : this.data.items;
21795         var v, sv, r = [], l = {};
21796         for(var i = 0, len = d.length; i < len; i++){
21797             v = d[i].data[dataIndex];
21798             sv = String(v);
21799             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21800                 l[sv] = true;
21801                 r[r.length] = v;
21802             }
21803         }
21804         return r;
21805     },
21806
21807     /**
21808      * Revert to a view of the Record cache with no filtering applied.
21809      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21810      */
21811     clearFilter : function(suppressEvent){
21812         if(this.snapshot && this.snapshot != this.data){
21813             this.data = this.snapshot;
21814             delete this.snapshot;
21815             if(suppressEvent !== true){
21816                 this.fireEvent("datachanged", this);
21817             }
21818         }
21819     },
21820
21821     // private
21822     afterEdit : function(record){
21823         if(this.modified.indexOf(record) == -1){
21824             this.modified.push(record);
21825         }
21826         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21827     },
21828     
21829     // private
21830     afterReject : function(record){
21831         this.modified.remove(record);
21832         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21833     },
21834
21835     // private
21836     afterCommit : function(record){
21837         this.modified.remove(record);
21838         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21839     },
21840
21841     /**
21842      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21843      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21844      */
21845     commitChanges : function(){
21846         var m = this.modified.slice(0);
21847         this.modified = [];
21848         for(var i = 0, len = m.length; i < len; i++){
21849             m[i].commit();
21850         }
21851     },
21852
21853     /**
21854      * Cancel outstanding changes on all changed records.
21855      */
21856     rejectChanges : function(){
21857         var m = this.modified.slice(0);
21858         this.modified = [];
21859         for(var i = 0, len = m.length; i < len; i++){
21860             m[i].reject();
21861         }
21862     },
21863
21864     onMetaChange : function(meta, rtype, o){
21865         this.recordType = rtype;
21866         this.fields = rtype.prototype.fields;
21867         delete this.snapshot;
21868         this.sortInfo = meta.sortInfo || this.sortInfo;
21869         this.modified = [];
21870         this.fireEvent('metachange', this, this.reader.meta);
21871     },
21872     
21873     moveIndex : function(data, type)
21874     {
21875         var index = this.indexOf(data);
21876         
21877         var newIndex = index + type;
21878         
21879         this.remove(data);
21880         
21881         this.insert(newIndex, data);
21882         
21883     }
21884 });/*
21885  * Based on:
21886  * Ext JS Library 1.1.1
21887  * Copyright(c) 2006-2007, Ext JS, LLC.
21888  *
21889  * Originally Released Under LGPL - original licence link has changed is not relivant.
21890  *
21891  * Fork - LGPL
21892  * <script type="text/javascript">
21893  */
21894
21895 /**
21896  * @class Roo.data.SimpleStore
21897  * @extends Roo.data.Store
21898  * Small helper class to make creating Stores from Array data easier.
21899  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21900  * @cfg {Array} fields An array of field definition objects, or field name strings.
21901  * @cfg {Array} data The multi-dimensional array of data
21902  * @constructor
21903  * @param {Object} config
21904  */
21905 Roo.data.SimpleStore = function(config){
21906     Roo.data.SimpleStore.superclass.constructor.call(this, {
21907         isLocal : true,
21908         reader: new Roo.data.ArrayReader({
21909                 id: config.id
21910             },
21911             Roo.data.Record.create(config.fields)
21912         ),
21913         proxy : new Roo.data.MemoryProxy(config.data)
21914     });
21915     this.load();
21916 };
21917 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21918  * Based on:
21919  * Ext JS Library 1.1.1
21920  * Copyright(c) 2006-2007, Ext JS, LLC.
21921  *
21922  * Originally Released Under LGPL - original licence link has changed is not relivant.
21923  *
21924  * Fork - LGPL
21925  * <script type="text/javascript">
21926  */
21927
21928 /**
21929 /**
21930  * @extends Roo.data.Store
21931  * @class Roo.data.JsonStore
21932  * Small helper class to make creating Stores for JSON data easier. <br/>
21933 <pre><code>
21934 var store = new Roo.data.JsonStore({
21935     url: 'get-images.php',
21936     root: 'images',
21937     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21938 });
21939 </code></pre>
21940  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21941  * JsonReader and HttpProxy (unless inline data is provided).</b>
21942  * @cfg {Array} fields An array of field definition objects, or field name strings.
21943  * @constructor
21944  * @param {Object} config
21945  */
21946 Roo.data.JsonStore = function(c){
21947     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21948         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21949         reader: new Roo.data.JsonReader(c, c.fields)
21950     }));
21951 };
21952 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21953  * Based on:
21954  * Ext JS Library 1.1.1
21955  * Copyright(c) 2006-2007, Ext JS, LLC.
21956  *
21957  * Originally Released Under LGPL - original licence link has changed is not relivant.
21958  *
21959  * Fork - LGPL
21960  * <script type="text/javascript">
21961  */
21962
21963  
21964 Roo.data.Field = function(config){
21965     if(typeof config == "string"){
21966         config = {name: config};
21967     }
21968     Roo.apply(this, config);
21969     
21970     if(!this.type){
21971         this.type = "auto";
21972     }
21973     
21974     var st = Roo.data.SortTypes;
21975     // named sortTypes are supported, here we look them up
21976     if(typeof this.sortType == "string"){
21977         this.sortType = st[this.sortType];
21978     }
21979     
21980     // set default sortType for strings and dates
21981     if(!this.sortType){
21982         switch(this.type){
21983             case "string":
21984                 this.sortType = st.asUCString;
21985                 break;
21986             case "date":
21987                 this.sortType = st.asDate;
21988                 break;
21989             default:
21990                 this.sortType = st.none;
21991         }
21992     }
21993
21994     // define once
21995     var stripRe = /[\$,%]/g;
21996
21997     // prebuilt conversion function for this field, instead of
21998     // switching every time we're reading a value
21999     if(!this.convert){
22000         var cv, dateFormat = this.dateFormat;
22001         switch(this.type){
22002             case "":
22003             case "auto":
22004             case undefined:
22005                 cv = function(v){ return v; };
22006                 break;
22007             case "string":
22008                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22009                 break;
22010             case "int":
22011                 cv = function(v){
22012                     return v !== undefined && v !== null && v !== '' ?
22013                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22014                     };
22015                 break;
22016             case "float":
22017                 cv = function(v){
22018                     return v !== undefined && v !== null && v !== '' ?
22019                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22020                     };
22021                 break;
22022             case "bool":
22023             case "boolean":
22024                 cv = function(v){ return v === true || v === "true" || v == 1; };
22025                 break;
22026             case "date":
22027                 cv = function(v){
22028                     if(!v){
22029                         return '';
22030                     }
22031                     if(v instanceof Date){
22032                         return v;
22033                     }
22034                     if(dateFormat){
22035                         if(dateFormat == "timestamp"){
22036                             return new Date(v*1000);
22037                         }
22038                         return Date.parseDate(v, dateFormat);
22039                     }
22040                     var parsed = Date.parse(v);
22041                     return parsed ? new Date(parsed) : null;
22042                 };
22043              break;
22044             
22045         }
22046         this.convert = cv;
22047     }
22048 };
22049
22050 Roo.data.Field.prototype = {
22051     dateFormat: null,
22052     defaultValue: "",
22053     mapping: null,
22054     sortType : null,
22055     sortDir : "ASC"
22056 };/*
22057  * Based on:
22058  * Ext JS Library 1.1.1
22059  * Copyright(c) 2006-2007, Ext JS, LLC.
22060  *
22061  * Originally Released Under LGPL - original licence link has changed is not relivant.
22062  *
22063  * Fork - LGPL
22064  * <script type="text/javascript">
22065  */
22066  
22067 // Base class for reading structured data from a data source.  This class is intended to be
22068 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22069
22070 /**
22071  * @class Roo.data.DataReader
22072  * Base class for reading structured data from a data source.  This class is intended to be
22073  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22074  */
22075
22076 Roo.data.DataReader = function(meta, recordType){
22077     
22078     this.meta = meta;
22079     
22080     this.recordType = recordType instanceof Array ? 
22081         Roo.data.Record.create(recordType) : recordType;
22082 };
22083
22084 Roo.data.DataReader.prototype = {
22085      /**
22086      * Create an empty record
22087      * @param {Object} data (optional) - overlay some values
22088      * @return {Roo.data.Record} record created.
22089      */
22090     newRow :  function(d) {
22091         var da =  {};
22092         this.recordType.prototype.fields.each(function(c) {
22093             switch( c.type) {
22094                 case 'int' : da[c.name] = 0; break;
22095                 case 'date' : da[c.name] = new Date(); break;
22096                 case 'float' : da[c.name] = 0.0; break;
22097                 case 'boolean' : da[c.name] = false; break;
22098                 default : da[c.name] = ""; break;
22099             }
22100             
22101         });
22102         return new this.recordType(Roo.apply(da, d));
22103     }
22104     
22105 };/*
22106  * Based on:
22107  * Ext JS Library 1.1.1
22108  * Copyright(c) 2006-2007, Ext JS, LLC.
22109  *
22110  * Originally Released Under LGPL - original licence link has changed is not relivant.
22111  *
22112  * Fork - LGPL
22113  * <script type="text/javascript">
22114  */
22115
22116 /**
22117  * @class Roo.data.DataProxy
22118  * @extends Roo.data.Observable
22119  * This class is an abstract base class for implementations which provide retrieval of
22120  * unformatted data objects.<br>
22121  * <p>
22122  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22123  * (of the appropriate type which knows how to parse the data object) to provide a block of
22124  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22125  * <p>
22126  * Custom implementations must implement the load method as described in
22127  * {@link Roo.data.HttpProxy#load}.
22128  */
22129 Roo.data.DataProxy = function(){
22130     this.addEvents({
22131         /**
22132          * @event beforeload
22133          * Fires before a network request is made to retrieve a data object.
22134          * @param {Object} This DataProxy object.
22135          * @param {Object} params The params parameter to the load function.
22136          */
22137         beforeload : true,
22138         /**
22139          * @event load
22140          * Fires before the load method's callback is called.
22141          * @param {Object} This DataProxy object.
22142          * @param {Object} o The data object.
22143          * @param {Object} arg The callback argument object passed to the load function.
22144          */
22145         load : true,
22146         /**
22147          * @event loadexception
22148          * Fires if an Exception occurs during data retrieval.
22149          * @param {Object} This DataProxy object.
22150          * @param {Object} o The data object.
22151          * @param {Object} arg The callback argument object passed to the load function.
22152          * @param {Object} e The Exception.
22153          */
22154         loadexception : true
22155     });
22156     Roo.data.DataProxy.superclass.constructor.call(this);
22157 };
22158
22159 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22160
22161     /**
22162      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22163      */
22164 /*
22165  * Based on:
22166  * Ext JS Library 1.1.1
22167  * Copyright(c) 2006-2007, Ext JS, LLC.
22168  *
22169  * Originally Released Under LGPL - original licence link has changed is not relivant.
22170  *
22171  * Fork - LGPL
22172  * <script type="text/javascript">
22173  */
22174 /**
22175  * @class Roo.data.MemoryProxy
22176  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22177  * to the Reader when its load method is called.
22178  * @constructor
22179  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22180  */
22181 Roo.data.MemoryProxy = function(data){
22182     if (data.data) {
22183         data = data.data;
22184     }
22185     Roo.data.MemoryProxy.superclass.constructor.call(this);
22186     this.data = data;
22187 };
22188
22189 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22190     /**
22191      * Load data from the requested source (in this case an in-memory
22192      * data object passed to the constructor), read the data object into
22193      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22194      * process that block using the passed callback.
22195      * @param {Object} params This parameter is not used by the MemoryProxy class.
22196      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22197      * object into a block of Roo.data.Records.
22198      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22199      * The function must be passed <ul>
22200      * <li>The Record block object</li>
22201      * <li>The "arg" argument from the load function</li>
22202      * <li>A boolean success indicator</li>
22203      * </ul>
22204      * @param {Object} scope The scope in which to call the callback
22205      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22206      */
22207     load : function(params, reader, callback, scope, arg){
22208         params = params || {};
22209         var result;
22210         try {
22211             result = reader.readRecords(this.data);
22212         }catch(e){
22213             this.fireEvent("loadexception", this, arg, null, e);
22214             callback.call(scope, null, arg, false);
22215             return;
22216         }
22217         callback.call(scope, result, arg, true);
22218     },
22219     
22220     // private
22221     update : function(params, records){
22222         
22223     }
22224 });/*
22225  * Based on:
22226  * Ext JS Library 1.1.1
22227  * Copyright(c) 2006-2007, Ext JS, LLC.
22228  *
22229  * Originally Released Under LGPL - original licence link has changed is not relivant.
22230  *
22231  * Fork - LGPL
22232  * <script type="text/javascript">
22233  */
22234 /**
22235  * @class Roo.data.HttpProxy
22236  * @extends Roo.data.DataProxy
22237  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22238  * configured to reference a certain URL.<br><br>
22239  * <p>
22240  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22241  * from which the running page was served.<br><br>
22242  * <p>
22243  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22244  * <p>
22245  * Be aware that to enable the browser to parse an XML document, the server must set
22246  * the Content-Type header in the HTTP response to "text/xml".
22247  * @constructor
22248  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22249  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22250  * will be used to make the request.
22251  */
22252 Roo.data.HttpProxy = function(conn){
22253     Roo.data.HttpProxy.superclass.constructor.call(this);
22254     // is conn a conn config or a real conn?
22255     this.conn = conn;
22256     this.useAjax = !conn || !conn.events;
22257   
22258 };
22259
22260 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22261     // thse are take from connection...
22262     
22263     /**
22264      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22265      */
22266     /**
22267      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22268      * extra parameters to each request made by this object. (defaults to undefined)
22269      */
22270     /**
22271      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22272      *  to each request made by this object. (defaults to undefined)
22273      */
22274     /**
22275      * @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)
22276      */
22277     /**
22278      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22279      */
22280      /**
22281      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22282      * @type Boolean
22283      */
22284   
22285
22286     /**
22287      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22288      * @type Boolean
22289      */
22290     /**
22291      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22292      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22293      * a finer-grained basis than the DataProxy events.
22294      */
22295     getConnection : function(){
22296         return this.useAjax ? Roo.Ajax : this.conn;
22297     },
22298
22299     /**
22300      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22301      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22302      * process that block using the passed callback.
22303      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22304      * for the request to the remote server.
22305      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22306      * object into a block of Roo.data.Records.
22307      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22308      * The function must be passed <ul>
22309      * <li>The Record block object</li>
22310      * <li>The "arg" argument from the load function</li>
22311      * <li>A boolean success indicator</li>
22312      * </ul>
22313      * @param {Object} scope The scope in which to call the callback
22314      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22315      */
22316     load : function(params, reader, callback, scope, arg){
22317         if(this.fireEvent("beforeload", this, params) !== false){
22318             var  o = {
22319                 params : params || {},
22320                 request: {
22321                     callback : callback,
22322                     scope : scope,
22323                     arg : arg
22324                 },
22325                 reader: reader,
22326                 callback : this.loadResponse,
22327                 scope: this
22328             };
22329             if(this.useAjax){
22330                 Roo.applyIf(o, this.conn);
22331                 if(this.activeRequest){
22332                     Roo.Ajax.abort(this.activeRequest);
22333                 }
22334                 this.activeRequest = Roo.Ajax.request(o);
22335             }else{
22336                 this.conn.request(o);
22337             }
22338         }else{
22339             callback.call(scope||this, null, arg, false);
22340         }
22341     },
22342
22343     // private
22344     loadResponse : function(o, success, response){
22345         delete this.activeRequest;
22346         if(!success){
22347             this.fireEvent("loadexception", this, o, response);
22348             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22349             return;
22350         }
22351         var result;
22352         try {
22353             result = o.reader.read(response);
22354         }catch(e){
22355             this.fireEvent("loadexception", this, o, response, e);
22356             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22357             return;
22358         }
22359         
22360         this.fireEvent("load", this, o, o.request.arg);
22361         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22362     },
22363
22364     // private
22365     update : function(dataSet){
22366
22367     },
22368
22369     // private
22370     updateResponse : function(dataSet){
22371
22372     }
22373 });/*
22374  * Based on:
22375  * Ext JS Library 1.1.1
22376  * Copyright(c) 2006-2007, Ext JS, LLC.
22377  *
22378  * Originally Released Under LGPL - original licence link has changed is not relivant.
22379  *
22380  * Fork - LGPL
22381  * <script type="text/javascript">
22382  */
22383
22384 /**
22385  * @class Roo.data.ScriptTagProxy
22386  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22387  * other than the originating domain of the running page.<br><br>
22388  * <p>
22389  * <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
22390  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22391  * <p>
22392  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22393  * source code that is used as the source inside a &lt;script> tag.<br><br>
22394  * <p>
22395  * In order for the browser to process the returned data, the server must wrap the data object
22396  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22397  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22398  * depending on whether the callback name was passed:
22399  * <p>
22400  * <pre><code>
22401 boolean scriptTag = false;
22402 String cb = request.getParameter("callback");
22403 if (cb != null) {
22404     scriptTag = true;
22405     response.setContentType("text/javascript");
22406 } else {
22407     response.setContentType("application/x-json");
22408 }
22409 Writer out = response.getWriter();
22410 if (scriptTag) {
22411     out.write(cb + "(");
22412 }
22413 out.print(dataBlock.toJsonString());
22414 if (scriptTag) {
22415     out.write(");");
22416 }
22417 </pre></code>
22418  *
22419  * @constructor
22420  * @param {Object} config A configuration object.
22421  */
22422 Roo.data.ScriptTagProxy = function(config){
22423     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22424     Roo.apply(this, config);
22425     this.head = document.getElementsByTagName("head")[0];
22426 };
22427
22428 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22429
22430 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22431     /**
22432      * @cfg {String} url The URL from which to request the data object.
22433      */
22434     /**
22435      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22436      */
22437     timeout : 30000,
22438     /**
22439      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22440      * the server the name of the callback function set up by the load call to process the returned data object.
22441      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22442      * javascript output which calls this named function passing the data object as its only parameter.
22443      */
22444     callbackParam : "callback",
22445     /**
22446      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22447      * name to the request.
22448      */
22449     nocache : true,
22450
22451     /**
22452      * Load data from the configured URL, read the data object into
22453      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22454      * process that block using the passed callback.
22455      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22456      * for the request to the remote server.
22457      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22458      * object into a block of Roo.data.Records.
22459      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22460      * The function must be passed <ul>
22461      * <li>The Record block object</li>
22462      * <li>The "arg" argument from the load function</li>
22463      * <li>A boolean success indicator</li>
22464      * </ul>
22465      * @param {Object} scope The scope in which to call the callback
22466      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22467      */
22468     load : function(params, reader, callback, scope, arg){
22469         if(this.fireEvent("beforeload", this, params) !== false){
22470
22471             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22472
22473             var url = this.url;
22474             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22475             if(this.nocache){
22476                 url += "&_dc=" + (new Date().getTime());
22477             }
22478             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22479             var trans = {
22480                 id : transId,
22481                 cb : "stcCallback"+transId,
22482                 scriptId : "stcScript"+transId,
22483                 params : params,
22484                 arg : arg,
22485                 url : url,
22486                 callback : callback,
22487                 scope : scope,
22488                 reader : reader
22489             };
22490             var conn = this;
22491
22492             window[trans.cb] = function(o){
22493                 conn.handleResponse(o, trans);
22494             };
22495
22496             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22497
22498             if(this.autoAbort !== false){
22499                 this.abort();
22500             }
22501
22502             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22503
22504             var script = document.createElement("script");
22505             script.setAttribute("src", url);
22506             script.setAttribute("type", "text/javascript");
22507             script.setAttribute("id", trans.scriptId);
22508             this.head.appendChild(script);
22509
22510             this.trans = trans;
22511         }else{
22512             callback.call(scope||this, null, arg, false);
22513         }
22514     },
22515
22516     // private
22517     isLoading : function(){
22518         return this.trans ? true : false;
22519     },
22520
22521     /**
22522      * Abort the current server request.
22523      */
22524     abort : function(){
22525         if(this.isLoading()){
22526             this.destroyTrans(this.trans);
22527         }
22528     },
22529
22530     // private
22531     destroyTrans : function(trans, isLoaded){
22532         this.head.removeChild(document.getElementById(trans.scriptId));
22533         clearTimeout(trans.timeoutId);
22534         if(isLoaded){
22535             window[trans.cb] = undefined;
22536             try{
22537                 delete window[trans.cb];
22538             }catch(e){}
22539         }else{
22540             // if hasn't been loaded, wait for load to remove it to prevent script error
22541             window[trans.cb] = function(){
22542                 window[trans.cb] = undefined;
22543                 try{
22544                     delete window[trans.cb];
22545                 }catch(e){}
22546             };
22547         }
22548     },
22549
22550     // private
22551     handleResponse : function(o, trans){
22552         this.trans = false;
22553         this.destroyTrans(trans, true);
22554         var result;
22555         try {
22556             result = trans.reader.readRecords(o);
22557         }catch(e){
22558             this.fireEvent("loadexception", this, o, trans.arg, e);
22559             trans.callback.call(trans.scope||window, null, trans.arg, false);
22560             return;
22561         }
22562         this.fireEvent("load", this, o, trans.arg);
22563         trans.callback.call(trans.scope||window, result, trans.arg, true);
22564     },
22565
22566     // private
22567     handleFailure : function(trans){
22568         this.trans = false;
22569         this.destroyTrans(trans, false);
22570         this.fireEvent("loadexception", this, null, trans.arg);
22571         trans.callback.call(trans.scope||window, null, trans.arg, false);
22572     }
22573 });/*
22574  * Based on:
22575  * Ext JS Library 1.1.1
22576  * Copyright(c) 2006-2007, Ext JS, LLC.
22577  *
22578  * Originally Released Under LGPL - original licence link has changed is not relivant.
22579  *
22580  * Fork - LGPL
22581  * <script type="text/javascript">
22582  */
22583
22584 /**
22585  * @class Roo.data.JsonReader
22586  * @extends Roo.data.DataReader
22587  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22588  * based on mappings in a provided Roo.data.Record constructor.
22589  * 
22590  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22591  * in the reply previously. 
22592  * 
22593  * <p>
22594  * Example code:
22595  * <pre><code>
22596 var RecordDef = Roo.data.Record.create([
22597     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22598     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22599 ]);
22600 var myReader = new Roo.data.JsonReader({
22601     totalProperty: "results",    // The property which contains the total dataset size (optional)
22602     root: "rows",                // The property which contains an Array of row objects
22603     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22604 }, RecordDef);
22605 </code></pre>
22606  * <p>
22607  * This would consume a JSON file like this:
22608  * <pre><code>
22609 { 'results': 2, 'rows': [
22610     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22611     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22612 }
22613 </code></pre>
22614  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22615  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22616  * paged from the remote server.
22617  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22618  * @cfg {String} root name of the property which contains the Array of row objects.
22619  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22620  * @cfg {Array} fields Array of field definition objects
22621  * @constructor
22622  * Create a new JsonReader
22623  * @param {Object} meta Metadata configuration options
22624  * @param {Object} recordType Either an Array of field definition objects,
22625  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22626  */
22627 Roo.data.JsonReader = function(meta, recordType){
22628     
22629     meta = meta || {};
22630     // set some defaults:
22631     Roo.applyIf(meta, {
22632         totalProperty: 'total',
22633         successProperty : 'success',
22634         root : 'data',
22635         id : 'id'
22636     });
22637     
22638     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22639 };
22640 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22641     
22642     /**
22643      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22644      * Used by Store query builder to append _requestMeta to params.
22645      * 
22646      */
22647     metaFromRemote : false,
22648     /**
22649      * This method is only used by a DataProxy which has retrieved data from a remote server.
22650      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22651      * @return {Object} data A data block which is used by an Roo.data.Store object as
22652      * a cache of Roo.data.Records.
22653      */
22654     read : function(response){
22655         var json = response.responseText;
22656        
22657         var o = /* eval:var:o */ eval("("+json+")");
22658         if(!o) {
22659             throw {message: "JsonReader.read: Json object not found"};
22660         }
22661         
22662         if(o.metaData){
22663             
22664             delete this.ef;
22665             this.metaFromRemote = true;
22666             this.meta = o.metaData;
22667             this.recordType = Roo.data.Record.create(o.metaData.fields);
22668             this.onMetaChange(this.meta, this.recordType, o);
22669         }
22670         return this.readRecords(o);
22671     },
22672
22673     // private function a store will implement
22674     onMetaChange : function(meta, recordType, o){
22675
22676     },
22677
22678     /**
22679          * @ignore
22680          */
22681     simpleAccess: function(obj, subsc) {
22682         return obj[subsc];
22683     },
22684
22685         /**
22686          * @ignore
22687          */
22688     getJsonAccessor: function(){
22689         var re = /[\[\.]/;
22690         return function(expr) {
22691             try {
22692                 return(re.test(expr))
22693                     ? new Function("obj", "return obj." + expr)
22694                     : function(obj){
22695                         return obj[expr];
22696                     };
22697             } catch(e){}
22698             return Roo.emptyFn;
22699         };
22700     }(),
22701
22702     /**
22703      * Create a data block containing Roo.data.Records from an XML document.
22704      * @param {Object} o An object which contains an Array of row objects in the property specified
22705      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22706      * which contains the total size of the dataset.
22707      * @return {Object} data A data block which is used by an Roo.data.Store object as
22708      * a cache of Roo.data.Records.
22709      */
22710     readRecords : function(o){
22711         /**
22712          * After any data loads, the raw JSON data is available for further custom processing.
22713          * @type Object
22714          */
22715         this.o = o;
22716         var s = this.meta, Record = this.recordType,
22717             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22718
22719 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22720         if (!this.ef) {
22721             if(s.totalProperty) {
22722                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22723                 }
22724                 if(s.successProperty) {
22725                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22726                 }
22727                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22728                 if (s.id) {
22729                         var g = this.getJsonAccessor(s.id);
22730                         this.getId = function(rec) {
22731                                 var r = g(rec);  
22732                                 return (r === undefined || r === "") ? null : r;
22733                         };
22734                 } else {
22735                         this.getId = function(){return null;};
22736                 }
22737             this.ef = [];
22738             for(var jj = 0; jj < fl; jj++){
22739                 f = fi[jj];
22740                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22741                 this.ef[jj] = this.getJsonAccessor(map);
22742             }
22743         }
22744
22745         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22746         if(s.totalProperty){
22747             var vt = parseInt(this.getTotal(o), 10);
22748             if(!isNaN(vt)){
22749                 totalRecords = vt;
22750             }
22751         }
22752         if(s.successProperty){
22753             var vs = this.getSuccess(o);
22754             if(vs === false || vs === 'false'){
22755                 success = false;
22756             }
22757         }
22758         var records = [];
22759         for(var i = 0; i < c; i++){
22760                 var n = root[i];
22761             var values = {};
22762             var id = this.getId(n);
22763             for(var j = 0; j < fl; j++){
22764                 f = fi[j];
22765             var v = this.ef[j](n);
22766             if (!f.convert) {
22767                 Roo.log('missing convert for ' + f.name);
22768                 Roo.log(f);
22769                 continue;
22770             }
22771             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22772             }
22773             var record = new Record(values, id);
22774             record.json = n;
22775             records[i] = record;
22776         }
22777         return {
22778             raw : o,
22779             success : success,
22780             records : records,
22781             totalRecords : totalRecords
22782         };
22783     }
22784 });/*
22785  * Based on:
22786  * Ext JS Library 1.1.1
22787  * Copyright(c) 2006-2007, Ext JS, LLC.
22788  *
22789  * Originally Released Under LGPL - original licence link has changed is not relivant.
22790  *
22791  * Fork - LGPL
22792  * <script type="text/javascript">
22793  */
22794
22795 /**
22796  * @class Roo.data.XmlReader
22797  * @extends Roo.data.DataReader
22798  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22799  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22800  * <p>
22801  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22802  * header in the HTTP response must be set to "text/xml".</em>
22803  * <p>
22804  * Example code:
22805  * <pre><code>
22806 var RecordDef = Roo.data.Record.create([
22807    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22808    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22809 ]);
22810 var myReader = new Roo.data.XmlReader({
22811    totalRecords: "results", // The element which contains the total dataset size (optional)
22812    record: "row",           // The repeated element which contains row information
22813    id: "id"                 // The element within the row that provides an ID for the record (optional)
22814 }, RecordDef);
22815 </code></pre>
22816  * <p>
22817  * This would consume an XML file like this:
22818  * <pre><code>
22819 &lt;?xml?>
22820 &lt;dataset>
22821  &lt;results>2&lt;/results>
22822  &lt;row>
22823    &lt;id>1&lt;/id>
22824    &lt;name>Bill&lt;/name>
22825    &lt;occupation>Gardener&lt;/occupation>
22826  &lt;/row>
22827  &lt;row>
22828    &lt;id>2&lt;/id>
22829    &lt;name>Ben&lt;/name>
22830    &lt;occupation>Horticulturalist&lt;/occupation>
22831  &lt;/row>
22832 &lt;/dataset>
22833 </code></pre>
22834  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22835  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22836  * paged from the remote server.
22837  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22838  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22839  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22840  * a record identifier value.
22841  * @constructor
22842  * Create a new XmlReader
22843  * @param {Object} meta Metadata configuration options
22844  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22845  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22846  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22847  */
22848 Roo.data.XmlReader = function(meta, recordType){
22849     meta = meta || {};
22850     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22851 };
22852 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22853     /**
22854      * This method is only used by a DataProxy which has retrieved data from a remote server.
22855          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22856          * to contain a method called 'responseXML' that returns an XML document object.
22857      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22858      * a cache of Roo.data.Records.
22859      */
22860     read : function(response){
22861         var doc = response.responseXML;
22862         if(!doc) {
22863             throw {message: "XmlReader.read: XML Document not available"};
22864         }
22865         return this.readRecords(doc);
22866     },
22867
22868     /**
22869      * Create a data block containing Roo.data.Records from an XML document.
22870          * @param {Object} doc A parsed XML document.
22871      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22872      * a cache of Roo.data.Records.
22873      */
22874     readRecords : function(doc){
22875         /**
22876          * After any data loads/reads, the raw XML Document is available for further custom processing.
22877          * @type XMLDocument
22878          */
22879         this.xmlData = doc;
22880         var root = doc.documentElement || doc;
22881         var q = Roo.DomQuery;
22882         var recordType = this.recordType, fields = recordType.prototype.fields;
22883         var sid = this.meta.id;
22884         var totalRecords = 0, success = true;
22885         if(this.meta.totalRecords){
22886             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22887         }
22888         
22889         if(this.meta.success){
22890             var sv = q.selectValue(this.meta.success, root, true);
22891             success = sv !== false && sv !== 'false';
22892         }
22893         var records = [];
22894         var ns = q.select(this.meta.record, root);
22895         for(var i = 0, len = ns.length; i < len; i++) {
22896                 var n = ns[i];
22897                 var values = {};
22898                 var id = sid ? q.selectValue(sid, n) : undefined;
22899                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22900                     var f = fields.items[j];
22901                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22902                     v = f.convert(v);
22903                     values[f.name] = v;
22904                 }
22905                 var record = new recordType(values, id);
22906                 record.node = n;
22907                 records[records.length] = record;
22908             }
22909
22910             return {
22911                 success : success,
22912                 records : records,
22913                 totalRecords : totalRecords || records.length
22914             };
22915     }
22916 });/*
22917  * Based on:
22918  * Ext JS Library 1.1.1
22919  * Copyright(c) 2006-2007, Ext JS, LLC.
22920  *
22921  * Originally Released Under LGPL - original licence link has changed is not relivant.
22922  *
22923  * Fork - LGPL
22924  * <script type="text/javascript">
22925  */
22926
22927 /**
22928  * @class Roo.data.ArrayReader
22929  * @extends Roo.data.DataReader
22930  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22931  * Each element of that Array represents a row of data fields. The
22932  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22933  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22934  * <p>
22935  * Example code:.
22936  * <pre><code>
22937 var RecordDef = Roo.data.Record.create([
22938     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22939     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22940 ]);
22941 var myReader = new Roo.data.ArrayReader({
22942     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22943 }, RecordDef);
22944 </code></pre>
22945  * <p>
22946  * This would consume an Array like this:
22947  * <pre><code>
22948 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22949   </code></pre>
22950  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22951  * @constructor
22952  * Create a new JsonReader
22953  * @param {Object} meta Metadata configuration options.
22954  * @param {Object} recordType Either an Array of field definition objects
22955  * as specified to {@link Roo.data.Record#create},
22956  * or an {@link Roo.data.Record} object
22957  * created using {@link Roo.data.Record#create}.
22958  */
22959 Roo.data.ArrayReader = function(meta, recordType){
22960     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22961 };
22962
22963 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22964     /**
22965      * Create a data block containing Roo.data.Records from an XML document.
22966      * @param {Object} o An Array of row objects which represents the dataset.
22967      * @return {Object} data A data block which is used by an Roo.data.Store object as
22968      * a cache of Roo.data.Records.
22969      */
22970     readRecords : function(o){
22971         var sid = this.meta ? this.meta.id : null;
22972         var recordType = this.recordType, fields = recordType.prototype.fields;
22973         var records = [];
22974         var root = o;
22975             for(var i = 0; i < root.length; i++){
22976                     var n = root[i];
22977                 var values = {};
22978                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22979                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22980                 var f = fields.items[j];
22981                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22982                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22983                 v = f.convert(v);
22984                 values[f.name] = v;
22985             }
22986                 var record = new recordType(values, id);
22987                 record.json = n;
22988                 records[records.length] = record;
22989             }
22990             return {
22991                 records : records,
22992                 totalRecords : records.length
22993             };
22994     }
22995 });/*
22996  * Based on:
22997  * Ext JS Library 1.1.1
22998  * Copyright(c) 2006-2007, Ext JS, LLC.
22999  *
23000  * Originally Released Under LGPL - original licence link has changed is not relivant.
23001  *
23002  * Fork - LGPL
23003  * <script type="text/javascript">
23004  */
23005
23006
23007 /**
23008  * @class Roo.data.Tree
23009  * @extends Roo.util.Observable
23010  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23011  * in the tree have most standard DOM functionality.
23012  * @constructor
23013  * @param {Node} root (optional) The root node
23014  */
23015 Roo.data.Tree = function(root){
23016    this.nodeHash = {};
23017    /**
23018     * The root node for this tree
23019     * @type Node
23020     */
23021    this.root = null;
23022    if(root){
23023        this.setRootNode(root);
23024    }
23025    this.addEvents({
23026        /**
23027         * @event append
23028         * Fires when a new child node is appended to a node in this tree.
23029         * @param {Tree} tree The owner tree
23030         * @param {Node} parent The parent node
23031         * @param {Node} node The newly appended node
23032         * @param {Number} index The index of the newly appended node
23033         */
23034        "append" : true,
23035        /**
23036         * @event remove
23037         * Fires when a child node is removed from a node in this tree.
23038         * @param {Tree} tree The owner tree
23039         * @param {Node} parent The parent node
23040         * @param {Node} node The child node removed
23041         */
23042        "remove" : true,
23043        /**
23044         * @event move
23045         * Fires when a node is moved to a new location in the tree
23046         * @param {Tree} tree The owner tree
23047         * @param {Node} node The node moved
23048         * @param {Node} oldParent The old parent of this node
23049         * @param {Node} newParent The new parent of this node
23050         * @param {Number} index The index it was moved to
23051         */
23052        "move" : true,
23053        /**
23054         * @event insert
23055         * Fires when a new child node is inserted in a node in this tree.
23056         * @param {Tree} tree The owner tree
23057         * @param {Node} parent The parent node
23058         * @param {Node} node The child node inserted
23059         * @param {Node} refNode The child node the node was inserted before
23060         */
23061        "insert" : true,
23062        /**
23063         * @event beforeappend
23064         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23065         * @param {Tree} tree The owner tree
23066         * @param {Node} parent The parent node
23067         * @param {Node} node The child node to be appended
23068         */
23069        "beforeappend" : true,
23070        /**
23071         * @event beforeremove
23072         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23073         * @param {Tree} tree The owner tree
23074         * @param {Node} parent The parent node
23075         * @param {Node} node The child node to be removed
23076         */
23077        "beforeremove" : true,
23078        /**
23079         * @event beforemove
23080         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23081         * @param {Tree} tree The owner tree
23082         * @param {Node} node The node being moved
23083         * @param {Node} oldParent The parent of the node
23084         * @param {Node} newParent The new parent the node is moving to
23085         * @param {Number} index The index it is being moved to
23086         */
23087        "beforemove" : true,
23088        /**
23089         * @event beforeinsert
23090         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23091         * @param {Tree} tree The owner tree
23092         * @param {Node} parent The parent node
23093         * @param {Node} node The child node to be inserted
23094         * @param {Node} refNode The child node the node is being inserted before
23095         */
23096        "beforeinsert" : true
23097    });
23098
23099     Roo.data.Tree.superclass.constructor.call(this);
23100 };
23101
23102 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23103     pathSeparator: "/",
23104
23105     proxyNodeEvent : function(){
23106         return this.fireEvent.apply(this, arguments);
23107     },
23108
23109     /**
23110      * Returns the root node for this tree.
23111      * @return {Node}
23112      */
23113     getRootNode : function(){
23114         return this.root;
23115     },
23116
23117     /**
23118      * Sets the root node for this tree.
23119      * @param {Node} node
23120      * @return {Node}
23121      */
23122     setRootNode : function(node){
23123         this.root = node;
23124         node.ownerTree = this;
23125         node.isRoot = true;
23126         this.registerNode(node);
23127         return node;
23128     },
23129
23130     /**
23131      * Gets a node in this tree by its id.
23132      * @param {String} id
23133      * @return {Node}
23134      */
23135     getNodeById : function(id){
23136         return this.nodeHash[id];
23137     },
23138
23139     registerNode : function(node){
23140         this.nodeHash[node.id] = node;
23141     },
23142
23143     unregisterNode : function(node){
23144         delete this.nodeHash[node.id];
23145     },
23146
23147     toString : function(){
23148         return "[Tree"+(this.id?" "+this.id:"")+"]";
23149     }
23150 });
23151
23152 /**
23153  * @class Roo.data.Node
23154  * @extends Roo.util.Observable
23155  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23156  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23157  * @constructor
23158  * @param {Object} attributes The attributes/config for the node
23159  */
23160 Roo.data.Node = function(attributes){
23161     /**
23162      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23163      * @type {Object}
23164      */
23165     this.attributes = attributes || {};
23166     this.leaf = this.attributes.leaf;
23167     /**
23168      * The node id. @type String
23169      */
23170     this.id = this.attributes.id;
23171     if(!this.id){
23172         this.id = Roo.id(null, "ynode-");
23173         this.attributes.id = this.id;
23174     }
23175      
23176     
23177     /**
23178      * All child nodes of this node. @type Array
23179      */
23180     this.childNodes = [];
23181     if(!this.childNodes.indexOf){ // indexOf is a must
23182         this.childNodes.indexOf = function(o){
23183             for(var i = 0, len = this.length; i < len; i++){
23184                 if(this[i] == o) {
23185                     return i;
23186                 }
23187             }
23188             return -1;
23189         };
23190     }
23191     /**
23192      * The parent node for this node. @type Node
23193      */
23194     this.parentNode = null;
23195     /**
23196      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23197      */
23198     this.firstChild = null;
23199     /**
23200      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23201      */
23202     this.lastChild = null;
23203     /**
23204      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23205      */
23206     this.previousSibling = null;
23207     /**
23208      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23209      */
23210     this.nextSibling = null;
23211
23212     this.addEvents({
23213        /**
23214         * @event append
23215         * Fires when a new child node is appended
23216         * @param {Tree} tree The owner tree
23217         * @param {Node} this This node
23218         * @param {Node} node The newly appended node
23219         * @param {Number} index The index of the newly appended node
23220         */
23221        "append" : true,
23222        /**
23223         * @event remove
23224         * Fires when a child node is removed
23225         * @param {Tree} tree The owner tree
23226         * @param {Node} this This node
23227         * @param {Node} node The removed node
23228         */
23229        "remove" : true,
23230        /**
23231         * @event move
23232         * Fires when this node is moved to a new location in the tree
23233         * @param {Tree} tree The owner tree
23234         * @param {Node} this This node
23235         * @param {Node} oldParent The old parent of this node
23236         * @param {Node} newParent The new parent of this node
23237         * @param {Number} index The index it was moved to
23238         */
23239        "move" : true,
23240        /**
23241         * @event insert
23242         * Fires when a new child node is inserted.
23243         * @param {Tree} tree The owner tree
23244         * @param {Node} this This node
23245         * @param {Node} node The child node inserted
23246         * @param {Node} refNode The child node the node was inserted before
23247         */
23248        "insert" : true,
23249        /**
23250         * @event beforeappend
23251         * Fires before a new child is appended, return false to cancel the append.
23252         * @param {Tree} tree The owner tree
23253         * @param {Node} this This node
23254         * @param {Node} node The child node to be appended
23255         */
23256        "beforeappend" : true,
23257        /**
23258         * @event beforeremove
23259         * Fires before a child is removed, return false to cancel the remove.
23260         * @param {Tree} tree The owner tree
23261         * @param {Node} this This node
23262         * @param {Node} node The child node to be removed
23263         */
23264        "beforeremove" : true,
23265        /**
23266         * @event beforemove
23267         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23268         * @param {Tree} tree The owner tree
23269         * @param {Node} this This node
23270         * @param {Node} oldParent The parent of this node
23271         * @param {Node} newParent The new parent this node is moving to
23272         * @param {Number} index The index it is being moved to
23273         */
23274        "beforemove" : true,
23275        /**
23276         * @event beforeinsert
23277         * Fires before a new child is inserted, return false to cancel the insert.
23278         * @param {Tree} tree The owner tree
23279         * @param {Node} this This node
23280         * @param {Node} node The child node to be inserted
23281         * @param {Node} refNode The child node the node is being inserted before
23282         */
23283        "beforeinsert" : true
23284    });
23285     this.listeners = this.attributes.listeners;
23286     Roo.data.Node.superclass.constructor.call(this);
23287 };
23288
23289 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23290     fireEvent : function(evtName){
23291         // first do standard event for this node
23292         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23293             return false;
23294         }
23295         // then bubble it up to the tree if the event wasn't cancelled
23296         var ot = this.getOwnerTree();
23297         if(ot){
23298             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23299                 return false;
23300             }
23301         }
23302         return true;
23303     },
23304
23305     /**
23306      * Returns true if this node is a leaf
23307      * @return {Boolean}
23308      */
23309     isLeaf : function(){
23310         return this.leaf === true;
23311     },
23312
23313     // private
23314     setFirstChild : function(node){
23315         this.firstChild = node;
23316     },
23317
23318     //private
23319     setLastChild : function(node){
23320         this.lastChild = node;
23321     },
23322
23323
23324     /**
23325      * Returns true if this node is the last child of its parent
23326      * @return {Boolean}
23327      */
23328     isLast : function(){
23329        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23330     },
23331
23332     /**
23333      * Returns true if this node is the first child of its parent
23334      * @return {Boolean}
23335      */
23336     isFirst : function(){
23337        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23338     },
23339
23340     hasChildNodes : function(){
23341         return !this.isLeaf() && this.childNodes.length > 0;
23342     },
23343
23344     /**
23345      * Insert node(s) as the last child node of this node.
23346      * @param {Node/Array} node The node or Array of nodes to append
23347      * @return {Node} The appended node if single append, or null if an array was passed
23348      */
23349     appendChild : function(node){
23350         var multi = false;
23351         if(node instanceof Array){
23352             multi = node;
23353         }else if(arguments.length > 1){
23354             multi = arguments;
23355         }
23356         // if passed an array or multiple args do them one by one
23357         if(multi){
23358             for(var i = 0, len = multi.length; i < len; i++) {
23359                 this.appendChild(multi[i]);
23360             }
23361         }else{
23362             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23363                 return false;
23364             }
23365             var index = this.childNodes.length;
23366             var oldParent = node.parentNode;
23367             // it's a move, make sure we move it cleanly
23368             if(oldParent){
23369                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23370                     return false;
23371                 }
23372                 oldParent.removeChild(node);
23373             }
23374             index = this.childNodes.length;
23375             if(index == 0){
23376                 this.setFirstChild(node);
23377             }
23378             this.childNodes.push(node);
23379             node.parentNode = this;
23380             var ps = this.childNodes[index-1];
23381             if(ps){
23382                 node.previousSibling = ps;
23383                 ps.nextSibling = node;
23384             }else{
23385                 node.previousSibling = null;
23386             }
23387             node.nextSibling = null;
23388             this.setLastChild(node);
23389             node.setOwnerTree(this.getOwnerTree());
23390             this.fireEvent("append", this.ownerTree, this, node, index);
23391             if(oldParent){
23392                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23393             }
23394             return node;
23395         }
23396     },
23397
23398     /**
23399      * Removes a child node from this node.
23400      * @param {Node} node The node to remove
23401      * @return {Node} The removed node
23402      */
23403     removeChild : function(node){
23404         var index = this.childNodes.indexOf(node);
23405         if(index == -1){
23406             return false;
23407         }
23408         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23409             return false;
23410         }
23411
23412         // remove it from childNodes collection
23413         this.childNodes.splice(index, 1);
23414
23415         // update siblings
23416         if(node.previousSibling){
23417             node.previousSibling.nextSibling = node.nextSibling;
23418         }
23419         if(node.nextSibling){
23420             node.nextSibling.previousSibling = node.previousSibling;
23421         }
23422
23423         // update child refs
23424         if(this.firstChild == node){
23425             this.setFirstChild(node.nextSibling);
23426         }
23427         if(this.lastChild == node){
23428             this.setLastChild(node.previousSibling);
23429         }
23430
23431         node.setOwnerTree(null);
23432         // clear any references from the node
23433         node.parentNode = null;
23434         node.previousSibling = null;
23435         node.nextSibling = null;
23436         this.fireEvent("remove", this.ownerTree, this, node);
23437         return node;
23438     },
23439
23440     /**
23441      * Inserts the first node before the second node in this nodes childNodes collection.
23442      * @param {Node} node The node to insert
23443      * @param {Node} refNode The node to insert before (if null the node is appended)
23444      * @return {Node} The inserted node
23445      */
23446     insertBefore : function(node, refNode){
23447         if(!refNode){ // like standard Dom, refNode can be null for append
23448             return this.appendChild(node);
23449         }
23450         // nothing to do
23451         if(node == refNode){
23452             return false;
23453         }
23454
23455         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23456             return false;
23457         }
23458         var index = this.childNodes.indexOf(refNode);
23459         var oldParent = node.parentNode;
23460         var refIndex = index;
23461
23462         // when moving internally, indexes will change after remove
23463         if(oldParent == this && this.childNodes.indexOf(node) < index){
23464             refIndex--;
23465         }
23466
23467         // it's a move, make sure we move it cleanly
23468         if(oldParent){
23469             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23470                 return false;
23471             }
23472             oldParent.removeChild(node);
23473         }
23474         if(refIndex == 0){
23475             this.setFirstChild(node);
23476         }
23477         this.childNodes.splice(refIndex, 0, node);
23478         node.parentNode = this;
23479         var ps = this.childNodes[refIndex-1];
23480         if(ps){
23481             node.previousSibling = ps;
23482             ps.nextSibling = node;
23483         }else{
23484             node.previousSibling = null;
23485         }
23486         node.nextSibling = refNode;
23487         refNode.previousSibling = node;
23488         node.setOwnerTree(this.getOwnerTree());
23489         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23490         if(oldParent){
23491             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23492         }
23493         return node;
23494     },
23495
23496     /**
23497      * Returns the child node at the specified index.
23498      * @param {Number} index
23499      * @return {Node}
23500      */
23501     item : function(index){
23502         return this.childNodes[index];
23503     },
23504
23505     /**
23506      * Replaces one child node in this node with another.
23507      * @param {Node} newChild The replacement node
23508      * @param {Node} oldChild The node to replace
23509      * @return {Node} The replaced node
23510      */
23511     replaceChild : function(newChild, oldChild){
23512         this.insertBefore(newChild, oldChild);
23513         this.removeChild(oldChild);
23514         return oldChild;
23515     },
23516
23517     /**
23518      * Returns the index of a child node
23519      * @param {Node} node
23520      * @return {Number} The index of the node or -1 if it was not found
23521      */
23522     indexOf : function(child){
23523         return this.childNodes.indexOf(child);
23524     },
23525
23526     /**
23527      * Returns the tree this node is in.
23528      * @return {Tree}
23529      */
23530     getOwnerTree : function(){
23531         // if it doesn't have one, look for one
23532         if(!this.ownerTree){
23533             var p = this;
23534             while(p){
23535                 if(p.ownerTree){
23536                     this.ownerTree = p.ownerTree;
23537                     break;
23538                 }
23539                 p = p.parentNode;
23540             }
23541         }
23542         return this.ownerTree;
23543     },
23544
23545     /**
23546      * Returns depth of this node (the root node has a depth of 0)
23547      * @return {Number}
23548      */
23549     getDepth : function(){
23550         var depth = 0;
23551         var p = this;
23552         while(p.parentNode){
23553             ++depth;
23554             p = p.parentNode;
23555         }
23556         return depth;
23557     },
23558
23559     // private
23560     setOwnerTree : function(tree){
23561         // if it's move, we need to update everyone
23562         if(tree != this.ownerTree){
23563             if(this.ownerTree){
23564                 this.ownerTree.unregisterNode(this);
23565             }
23566             this.ownerTree = tree;
23567             var cs = this.childNodes;
23568             for(var i = 0, len = cs.length; i < len; i++) {
23569                 cs[i].setOwnerTree(tree);
23570             }
23571             if(tree){
23572                 tree.registerNode(this);
23573             }
23574         }
23575     },
23576
23577     /**
23578      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23579      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23580      * @return {String} The path
23581      */
23582     getPath : function(attr){
23583         attr = attr || "id";
23584         var p = this.parentNode;
23585         var b = [this.attributes[attr]];
23586         while(p){
23587             b.unshift(p.attributes[attr]);
23588             p = p.parentNode;
23589         }
23590         var sep = this.getOwnerTree().pathSeparator;
23591         return sep + b.join(sep);
23592     },
23593
23594     /**
23595      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23596      * function call will be the scope provided or the current node. The arguments to the function
23597      * will be the args provided or the current node. If the function returns false at any point,
23598      * the bubble is stopped.
23599      * @param {Function} fn The function to call
23600      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23601      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23602      */
23603     bubble : function(fn, scope, args){
23604         var p = this;
23605         while(p){
23606             if(fn.call(scope || p, args || p) === false){
23607                 break;
23608             }
23609             p = p.parentNode;
23610         }
23611     },
23612
23613     /**
23614      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23615      * function call will be the scope provided or the current node. The arguments to the function
23616      * will be the args provided or the current node. If the function returns false at any point,
23617      * the cascade is stopped on that branch.
23618      * @param {Function} fn The function to call
23619      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23620      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23621      */
23622     cascade : function(fn, scope, args){
23623         if(fn.call(scope || this, args || this) !== false){
23624             var cs = this.childNodes;
23625             for(var i = 0, len = cs.length; i < len; i++) {
23626                 cs[i].cascade(fn, scope, args);
23627             }
23628         }
23629     },
23630
23631     /**
23632      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23633      * function call will be the scope provided or the current node. The arguments to the function
23634      * will be the args provided or the current node. If the function returns false at any point,
23635      * the iteration stops.
23636      * @param {Function} fn The function to call
23637      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23638      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23639      */
23640     eachChild : function(fn, scope, args){
23641         var cs = this.childNodes;
23642         for(var i = 0, len = cs.length; i < len; i++) {
23643                 if(fn.call(scope || this, args || cs[i]) === false){
23644                     break;
23645                 }
23646         }
23647     },
23648
23649     /**
23650      * Finds the first child that has the attribute with the specified value.
23651      * @param {String} attribute The attribute name
23652      * @param {Mixed} value The value to search for
23653      * @return {Node} The found child or null if none was found
23654      */
23655     findChild : function(attribute, value){
23656         var cs = this.childNodes;
23657         for(var i = 0, len = cs.length; i < len; i++) {
23658                 if(cs[i].attributes[attribute] == value){
23659                     return cs[i];
23660                 }
23661         }
23662         return null;
23663     },
23664
23665     /**
23666      * Finds the first child by a custom function. The child matches if the function passed
23667      * returns true.
23668      * @param {Function} fn
23669      * @param {Object} scope (optional)
23670      * @return {Node} The found child or null if none was found
23671      */
23672     findChildBy : function(fn, scope){
23673         var cs = this.childNodes;
23674         for(var i = 0, len = cs.length; i < len; i++) {
23675                 if(fn.call(scope||cs[i], cs[i]) === true){
23676                     return cs[i];
23677                 }
23678         }
23679         return null;
23680     },
23681
23682     /**
23683      * Sorts this nodes children using the supplied sort function
23684      * @param {Function} fn
23685      * @param {Object} scope (optional)
23686      */
23687     sort : function(fn, scope){
23688         var cs = this.childNodes;
23689         var len = cs.length;
23690         if(len > 0){
23691             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23692             cs.sort(sortFn);
23693             for(var i = 0; i < len; i++){
23694                 var n = cs[i];
23695                 n.previousSibling = cs[i-1];
23696                 n.nextSibling = cs[i+1];
23697                 if(i == 0){
23698                     this.setFirstChild(n);
23699                 }
23700                 if(i == len-1){
23701                     this.setLastChild(n);
23702                 }
23703             }
23704         }
23705     },
23706
23707     /**
23708      * Returns true if this node is an ancestor (at any point) of the passed node.
23709      * @param {Node} node
23710      * @return {Boolean}
23711      */
23712     contains : function(node){
23713         return node.isAncestor(this);
23714     },
23715
23716     /**
23717      * Returns true if the passed node is an ancestor (at any point) of this node.
23718      * @param {Node} node
23719      * @return {Boolean}
23720      */
23721     isAncestor : function(node){
23722         var p = this.parentNode;
23723         while(p){
23724             if(p == node){
23725                 return true;
23726             }
23727             p = p.parentNode;
23728         }
23729         return false;
23730     },
23731
23732     toString : function(){
23733         return "[Node"+(this.id?" "+this.id:"")+"]";
23734     }
23735 });/*
23736  * Based on:
23737  * Ext JS Library 1.1.1
23738  * Copyright(c) 2006-2007, Ext JS, LLC.
23739  *
23740  * Originally Released Under LGPL - original licence link has changed is not relivant.
23741  *
23742  * Fork - LGPL
23743  * <script type="text/javascript">
23744  */
23745  (function(){ 
23746 /**
23747  * @class Roo.Layer
23748  * @extends Roo.Element
23749  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23750  * automatic maintaining of shadow/shim positions.
23751  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23752  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23753  * you can pass a string with a CSS class name. False turns off the shadow.
23754  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23755  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23756  * @cfg {String} cls CSS class to add to the element
23757  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23758  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23759  * @constructor
23760  * @param {Object} config An object with config options.
23761  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23762  */
23763
23764 Roo.Layer = function(config, existingEl){
23765     config = config || {};
23766     var dh = Roo.DomHelper;
23767     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23768     if(existingEl){
23769         this.dom = Roo.getDom(existingEl);
23770     }
23771     if(!this.dom){
23772         var o = config.dh || {tag: "div", cls: "x-layer"};
23773         this.dom = dh.append(pel, o);
23774     }
23775     if(config.cls){
23776         this.addClass(config.cls);
23777     }
23778     this.constrain = config.constrain !== false;
23779     this.visibilityMode = Roo.Element.VISIBILITY;
23780     if(config.id){
23781         this.id = this.dom.id = config.id;
23782     }else{
23783         this.id = Roo.id(this.dom);
23784     }
23785     this.zindex = config.zindex || this.getZIndex();
23786     this.position("absolute", this.zindex);
23787     if(config.shadow){
23788         this.shadowOffset = config.shadowOffset || 4;
23789         this.shadow = new Roo.Shadow({
23790             offset : this.shadowOffset,
23791             mode : config.shadow
23792         });
23793     }else{
23794         this.shadowOffset = 0;
23795     }
23796     this.useShim = config.shim !== false && Roo.useShims;
23797     this.useDisplay = config.useDisplay;
23798     this.hide();
23799 };
23800
23801 var supr = Roo.Element.prototype;
23802
23803 // shims are shared among layer to keep from having 100 iframes
23804 var shims = [];
23805
23806 Roo.extend(Roo.Layer, Roo.Element, {
23807
23808     getZIndex : function(){
23809         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23810     },
23811
23812     getShim : function(){
23813         if(!this.useShim){
23814             return null;
23815         }
23816         if(this.shim){
23817             return this.shim;
23818         }
23819         var shim = shims.shift();
23820         if(!shim){
23821             shim = this.createShim();
23822             shim.enableDisplayMode('block');
23823             shim.dom.style.display = 'none';
23824             shim.dom.style.visibility = 'visible';
23825         }
23826         var pn = this.dom.parentNode;
23827         if(shim.dom.parentNode != pn){
23828             pn.insertBefore(shim.dom, this.dom);
23829         }
23830         shim.setStyle('z-index', this.getZIndex()-2);
23831         this.shim = shim;
23832         return shim;
23833     },
23834
23835     hideShim : function(){
23836         if(this.shim){
23837             this.shim.setDisplayed(false);
23838             shims.push(this.shim);
23839             delete this.shim;
23840         }
23841     },
23842
23843     disableShadow : function(){
23844         if(this.shadow){
23845             this.shadowDisabled = true;
23846             this.shadow.hide();
23847             this.lastShadowOffset = this.shadowOffset;
23848             this.shadowOffset = 0;
23849         }
23850     },
23851
23852     enableShadow : function(show){
23853         if(this.shadow){
23854             this.shadowDisabled = false;
23855             this.shadowOffset = this.lastShadowOffset;
23856             delete this.lastShadowOffset;
23857             if(show){
23858                 this.sync(true);
23859             }
23860         }
23861     },
23862
23863     // private
23864     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23865     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23866     sync : function(doShow){
23867         var sw = this.shadow;
23868         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23869             var sh = this.getShim();
23870
23871             var w = this.getWidth(),
23872                 h = this.getHeight();
23873
23874             var l = this.getLeft(true),
23875                 t = this.getTop(true);
23876
23877             if(sw && !this.shadowDisabled){
23878                 if(doShow && !sw.isVisible()){
23879                     sw.show(this);
23880                 }else{
23881                     sw.realign(l, t, w, h);
23882                 }
23883                 if(sh){
23884                     if(doShow){
23885                        sh.show();
23886                     }
23887                     // fit the shim behind the shadow, so it is shimmed too
23888                     var a = sw.adjusts, s = sh.dom.style;
23889                     s.left = (Math.min(l, l+a.l))+"px";
23890                     s.top = (Math.min(t, t+a.t))+"px";
23891                     s.width = (w+a.w)+"px";
23892                     s.height = (h+a.h)+"px";
23893                 }
23894             }else if(sh){
23895                 if(doShow){
23896                    sh.show();
23897                 }
23898                 sh.setSize(w, h);
23899                 sh.setLeftTop(l, t);
23900             }
23901             
23902         }
23903     },
23904
23905     // private
23906     destroy : function(){
23907         this.hideShim();
23908         if(this.shadow){
23909             this.shadow.hide();
23910         }
23911         this.removeAllListeners();
23912         var pn = this.dom.parentNode;
23913         if(pn){
23914             pn.removeChild(this.dom);
23915         }
23916         Roo.Element.uncache(this.id);
23917     },
23918
23919     remove : function(){
23920         this.destroy();
23921     },
23922
23923     // private
23924     beginUpdate : function(){
23925         this.updating = true;
23926     },
23927
23928     // private
23929     endUpdate : function(){
23930         this.updating = false;
23931         this.sync(true);
23932     },
23933
23934     // private
23935     hideUnders : function(negOffset){
23936         if(this.shadow){
23937             this.shadow.hide();
23938         }
23939         this.hideShim();
23940     },
23941
23942     // private
23943     constrainXY : function(){
23944         if(this.constrain){
23945             var vw = Roo.lib.Dom.getViewWidth(),
23946                 vh = Roo.lib.Dom.getViewHeight();
23947             var s = Roo.get(document).getScroll();
23948
23949             var xy = this.getXY();
23950             var x = xy[0], y = xy[1];   
23951             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23952             // only move it if it needs it
23953             var moved = false;
23954             // first validate right/bottom
23955             if((x + w) > vw+s.left){
23956                 x = vw - w - this.shadowOffset;
23957                 moved = true;
23958             }
23959             if((y + h) > vh+s.top){
23960                 y = vh - h - this.shadowOffset;
23961                 moved = true;
23962             }
23963             // then make sure top/left isn't negative
23964             if(x < s.left){
23965                 x = s.left;
23966                 moved = true;
23967             }
23968             if(y < s.top){
23969                 y = s.top;
23970                 moved = true;
23971             }
23972             if(moved){
23973                 if(this.avoidY){
23974                     var ay = this.avoidY;
23975                     if(y <= ay && (y+h) >= ay){
23976                         y = ay-h-5;   
23977                     }
23978                 }
23979                 xy = [x, y];
23980                 this.storeXY(xy);
23981                 supr.setXY.call(this, xy);
23982                 this.sync();
23983             }
23984         }
23985     },
23986
23987     isVisible : function(){
23988         return this.visible;    
23989     },
23990
23991     // private
23992     showAction : function(){
23993         this.visible = true; // track visibility to prevent getStyle calls
23994         if(this.useDisplay === true){
23995             this.setDisplayed("");
23996         }else if(this.lastXY){
23997             supr.setXY.call(this, this.lastXY);
23998         }else if(this.lastLT){
23999             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
24000         }
24001     },
24002
24003     // private
24004     hideAction : function(){
24005         this.visible = false;
24006         if(this.useDisplay === true){
24007             this.setDisplayed(false);
24008         }else{
24009             this.setLeftTop(-10000,-10000);
24010         }
24011     },
24012
24013     // overridden Element method
24014     setVisible : function(v, a, d, c, e){
24015         if(v){
24016             this.showAction();
24017         }
24018         if(a && v){
24019             var cb = function(){
24020                 this.sync(true);
24021                 if(c){
24022                     c();
24023                 }
24024             }.createDelegate(this);
24025             supr.setVisible.call(this, true, true, d, cb, e);
24026         }else{
24027             if(!v){
24028                 this.hideUnders(true);
24029             }
24030             var cb = c;
24031             if(a){
24032                 cb = function(){
24033                     this.hideAction();
24034                     if(c){
24035                         c();
24036                     }
24037                 }.createDelegate(this);
24038             }
24039             supr.setVisible.call(this, v, a, d, cb, e);
24040             if(v){
24041                 this.sync(true);
24042             }else if(!a){
24043                 this.hideAction();
24044             }
24045         }
24046     },
24047
24048     storeXY : function(xy){
24049         delete this.lastLT;
24050         this.lastXY = xy;
24051     },
24052
24053     storeLeftTop : function(left, top){
24054         delete this.lastXY;
24055         this.lastLT = [left, top];
24056     },
24057
24058     // private
24059     beforeFx : function(){
24060         this.beforeAction();
24061         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24062     },
24063
24064     // private
24065     afterFx : function(){
24066         Roo.Layer.superclass.afterFx.apply(this, arguments);
24067         this.sync(this.isVisible());
24068     },
24069
24070     // private
24071     beforeAction : function(){
24072         if(!this.updating && this.shadow){
24073             this.shadow.hide();
24074         }
24075     },
24076
24077     // overridden Element method
24078     setLeft : function(left){
24079         this.storeLeftTop(left, this.getTop(true));
24080         supr.setLeft.apply(this, arguments);
24081         this.sync();
24082     },
24083
24084     setTop : function(top){
24085         this.storeLeftTop(this.getLeft(true), top);
24086         supr.setTop.apply(this, arguments);
24087         this.sync();
24088     },
24089
24090     setLeftTop : function(left, top){
24091         this.storeLeftTop(left, top);
24092         supr.setLeftTop.apply(this, arguments);
24093         this.sync();
24094     },
24095
24096     setXY : function(xy, a, d, c, e){
24097         this.fixDisplay();
24098         this.beforeAction();
24099         this.storeXY(xy);
24100         var cb = this.createCB(c);
24101         supr.setXY.call(this, xy, a, d, cb, e);
24102         if(!a){
24103             cb();
24104         }
24105     },
24106
24107     // private
24108     createCB : function(c){
24109         var el = this;
24110         return function(){
24111             el.constrainXY();
24112             el.sync(true);
24113             if(c){
24114                 c();
24115             }
24116         };
24117     },
24118
24119     // overridden Element method
24120     setX : function(x, a, d, c, e){
24121         this.setXY([x, this.getY()], a, d, c, e);
24122     },
24123
24124     // overridden Element method
24125     setY : function(y, a, d, c, e){
24126         this.setXY([this.getX(), y], a, d, c, e);
24127     },
24128
24129     // overridden Element method
24130     setSize : function(w, h, a, d, c, e){
24131         this.beforeAction();
24132         var cb = this.createCB(c);
24133         supr.setSize.call(this, w, h, a, d, cb, e);
24134         if(!a){
24135             cb();
24136         }
24137     },
24138
24139     // overridden Element method
24140     setWidth : function(w, a, d, c, e){
24141         this.beforeAction();
24142         var cb = this.createCB(c);
24143         supr.setWidth.call(this, w, a, d, cb, e);
24144         if(!a){
24145             cb();
24146         }
24147     },
24148
24149     // overridden Element method
24150     setHeight : function(h, a, d, c, e){
24151         this.beforeAction();
24152         var cb = this.createCB(c);
24153         supr.setHeight.call(this, h, a, d, cb, e);
24154         if(!a){
24155             cb();
24156         }
24157     },
24158
24159     // overridden Element method
24160     setBounds : function(x, y, w, h, a, d, c, e){
24161         this.beforeAction();
24162         var cb = this.createCB(c);
24163         if(!a){
24164             this.storeXY([x, y]);
24165             supr.setXY.call(this, [x, y]);
24166             supr.setSize.call(this, w, h, a, d, cb, e);
24167             cb();
24168         }else{
24169             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24170         }
24171         return this;
24172     },
24173     
24174     /**
24175      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24176      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24177      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24178      * @param {Number} zindex The new z-index to set
24179      * @return {this} The Layer
24180      */
24181     setZIndex : function(zindex){
24182         this.zindex = zindex;
24183         this.setStyle("z-index", zindex + 2);
24184         if(this.shadow){
24185             this.shadow.setZIndex(zindex + 1);
24186         }
24187         if(this.shim){
24188             this.shim.setStyle("z-index", zindex);
24189         }
24190     }
24191 });
24192 })();/*
24193  * Based on:
24194  * Ext JS Library 1.1.1
24195  * Copyright(c) 2006-2007, Ext JS, LLC.
24196  *
24197  * Originally Released Under LGPL - original licence link has changed is not relivant.
24198  *
24199  * Fork - LGPL
24200  * <script type="text/javascript">
24201  */
24202
24203
24204 /**
24205  * @class Roo.Shadow
24206  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24207  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24208  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24209  * @constructor
24210  * Create a new Shadow
24211  * @param {Object} config The config object
24212  */
24213 Roo.Shadow = function(config){
24214     Roo.apply(this, config);
24215     if(typeof this.mode != "string"){
24216         this.mode = this.defaultMode;
24217     }
24218     var o = this.offset, a = {h: 0};
24219     var rad = Math.floor(this.offset/2);
24220     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24221         case "drop":
24222             a.w = 0;
24223             a.l = a.t = o;
24224             a.t -= 1;
24225             if(Roo.isIE){
24226                 a.l -= this.offset + rad;
24227                 a.t -= this.offset + rad;
24228                 a.w -= rad;
24229                 a.h -= rad;
24230                 a.t += 1;
24231             }
24232         break;
24233         case "sides":
24234             a.w = (o*2);
24235             a.l = -o;
24236             a.t = o-1;
24237             if(Roo.isIE){
24238                 a.l -= (this.offset - rad);
24239                 a.t -= this.offset + rad;
24240                 a.l += 1;
24241                 a.w -= (this.offset - rad)*2;
24242                 a.w -= rad + 1;
24243                 a.h -= 1;
24244             }
24245         break;
24246         case "frame":
24247             a.w = a.h = (o*2);
24248             a.l = a.t = -o;
24249             a.t += 1;
24250             a.h -= 2;
24251             if(Roo.isIE){
24252                 a.l -= (this.offset - rad);
24253                 a.t -= (this.offset - rad);
24254                 a.l += 1;
24255                 a.w -= (this.offset + rad + 1);
24256                 a.h -= (this.offset + rad);
24257                 a.h += 1;
24258             }
24259         break;
24260     };
24261
24262     this.adjusts = a;
24263 };
24264
24265 Roo.Shadow.prototype = {
24266     /**
24267      * @cfg {String} mode
24268      * The shadow display mode.  Supports the following options:<br />
24269      * sides: Shadow displays on both sides and bottom only<br />
24270      * frame: Shadow displays equally on all four sides<br />
24271      * drop: Traditional bottom-right drop shadow (default)
24272      */
24273     /**
24274      * @cfg {String} offset
24275      * The number of pixels to offset the shadow from the element (defaults to 4)
24276      */
24277     offset: 4,
24278
24279     // private
24280     defaultMode: "drop",
24281
24282     /**
24283      * Displays the shadow under the target element
24284      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24285      */
24286     show : function(target){
24287         target = Roo.get(target);
24288         if(!this.el){
24289             this.el = Roo.Shadow.Pool.pull();
24290             if(this.el.dom.nextSibling != target.dom){
24291                 this.el.insertBefore(target);
24292             }
24293         }
24294         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24295         if(Roo.isIE){
24296             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24297         }
24298         this.realign(
24299             target.getLeft(true),
24300             target.getTop(true),
24301             target.getWidth(),
24302             target.getHeight()
24303         );
24304         this.el.dom.style.display = "block";
24305     },
24306
24307     /**
24308      * Returns true if the shadow is visible, else false
24309      */
24310     isVisible : function(){
24311         return this.el ? true : false;  
24312     },
24313
24314     /**
24315      * Direct alignment when values are already available. Show must be called at least once before
24316      * calling this method to ensure it is initialized.
24317      * @param {Number} left The target element left position
24318      * @param {Number} top The target element top position
24319      * @param {Number} width The target element width
24320      * @param {Number} height The target element height
24321      */
24322     realign : function(l, t, w, h){
24323         if(!this.el){
24324             return;
24325         }
24326         var a = this.adjusts, d = this.el.dom, s = d.style;
24327         var iea = 0;
24328         s.left = (l+a.l)+"px";
24329         s.top = (t+a.t)+"px";
24330         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24331  
24332         if(s.width != sws || s.height != shs){
24333             s.width = sws;
24334             s.height = shs;
24335             if(!Roo.isIE){
24336                 var cn = d.childNodes;
24337                 var sww = Math.max(0, (sw-12))+"px";
24338                 cn[0].childNodes[1].style.width = sww;
24339                 cn[1].childNodes[1].style.width = sww;
24340                 cn[2].childNodes[1].style.width = sww;
24341                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24342             }
24343         }
24344     },
24345
24346     /**
24347      * Hides this shadow
24348      */
24349     hide : function(){
24350         if(this.el){
24351             this.el.dom.style.display = "none";
24352             Roo.Shadow.Pool.push(this.el);
24353             delete this.el;
24354         }
24355     },
24356
24357     /**
24358      * Adjust the z-index of this shadow
24359      * @param {Number} zindex The new z-index
24360      */
24361     setZIndex : function(z){
24362         this.zIndex = z;
24363         if(this.el){
24364             this.el.setStyle("z-index", z);
24365         }
24366     }
24367 };
24368
24369 // Private utility class that manages the internal Shadow cache
24370 Roo.Shadow.Pool = function(){
24371     var p = [];
24372     var markup = Roo.isIE ?
24373                  '<div class="x-ie-shadow"></div>' :
24374                  '<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>';
24375     return {
24376         pull : function(){
24377             var sh = p.shift();
24378             if(!sh){
24379                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24380                 sh.autoBoxAdjust = false;
24381             }
24382             return sh;
24383         },
24384
24385         push : function(sh){
24386             p.push(sh);
24387         }
24388     };
24389 }();/*
24390  * Based on:
24391  * Ext JS Library 1.1.1
24392  * Copyright(c) 2006-2007, Ext JS, LLC.
24393  *
24394  * Originally Released Under LGPL - original licence link has changed is not relivant.
24395  *
24396  * Fork - LGPL
24397  * <script type="text/javascript">
24398  */
24399
24400
24401 /**
24402  * @class Roo.SplitBar
24403  * @extends Roo.util.Observable
24404  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24405  * <br><br>
24406  * Usage:
24407  * <pre><code>
24408 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24409                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24410 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24411 split.minSize = 100;
24412 split.maxSize = 600;
24413 split.animate = true;
24414 split.on('moved', splitterMoved);
24415 </code></pre>
24416  * @constructor
24417  * Create a new SplitBar
24418  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24419  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24420  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24421  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24422                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24423                         position of the SplitBar).
24424  */
24425 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24426     
24427     /** @private */
24428     this.el = Roo.get(dragElement, true);
24429     this.el.dom.unselectable = "on";
24430     /** @private */
24431     this.resizingEl = Roo.get(resizingElement, true);
24432
24433     /**
24434      * @private
24435      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24436      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24437      * @type Number
24438      */
24439     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24440     
24441     /**
24442      * The minimum size of the resizing element. (Defaults to 0)
24443      * @type Number
24444      */
24445     this.minSize = 0;
24446     
24447     /**
24448      * The maximum size of the resizing element. (Defaults to 2000)
24449      * @type Number
24450      */
24451     this.maxSize = 2000;
24452     
24453     /**
24454      * Whether to animate the transition to the new size
24455      * @type Boolean
24456      */
24457     this.animate = false;
24458     
24459     /**
24460      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24461      * @type Boolean
24462      */
24463     this.useShim = false;
24464     
24465     /** @private */
24466     this.shim = null;
24467     
24468     if(!existingProxy){
24469         /** @private */
24470         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24471     }else{
24472         this.proxy = Roo.get(existingProxy).dom;
24473     }
24474     /** @private */
24475     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24476     
24477     /** @private */
24478     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24479     
24480     /** @private */
24481     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24482     
24483     /** @private */
24484     this.dragSpecs = {};
24485     
24486     /**
24487      * @private The adapter to use to positon and resize elements
24488      */
24489     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24490     this.adapter.init(this);
24491     
24492     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24493         /** @private */
24494         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24495         this.el.addClass("x-splitbar-h");
24496     }else{
24497         /** @private */
24498         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24499         this.el.addClass("x-splitbar-v");
24500     }
24501     
24502     this.addEvents({
24503         /**
24504          * @event resize
24505          * Fires when the splitter is moved (alias for {@link #event-moved})
24506          * @param {Roo.SplitBar} this
24507          * @param {Number} newSize the new width or height
24508          */
24509         "resize" : true,
24510         /**
24511          * @event moved
24512          * Fires when the splitter is moved
24513          * @param {Roo.SplitBar} this
24514          * @param {Number} newSize the new width or height
24515          */
24516         "moved" : true,
24517         /**
24518          * @event beforeresize
24519          * Fires before the splitter is dragged
24520          * @param {Roo.SplitBar} this
24521          */
24522         "beforeresize" : true,
24523
24524         "beforeapply" : true
24525     });
24526
24527     Roo.util.Observable.call(this);
24528 };
24529
24530 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24531     onStartProxyDrag : function(x, y){
24532         this.fireEvent("beforeresize", this);
24533         if(!this.overlay){
24534             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24535             o.unselectable();
24536             o.enableDisplayMode("block");
24537             // all splitbars share the same overlay
24538             Roo.SplitBar.prototype.overlay = o;
24539         }
24540         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24541         this.overlay.show();
24542         Roo.get(this.proxy).setDisplayed("block");
24543         var size = this.adapter.getElementSize(this);
24544         this.activeMinSize = this.getMinimumSize();;
24545         this.activeMaxSize = this.getMaximumSize();;
24546         var c1 = size - this.activeMinSize;
24547         var c2 = Math.max(this.activeMaxSize - size, 0);
24548         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24549             this.dd.resetConstraints();
24550             this.dd.setXConstraint(
24551                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24552                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24553             );
24554             this.dd.setYConstraint(0, 0);
24555         }else{
24556             this.dd.resetConstraints();
24557             this.dd.setXConstraint(0, 0);
24558             this.dd.setYConstraint(
24559                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24560                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24561             );
24562          }
24563         this.dragSpecs.startSize = size;
24564         this.dragSpecs.startPoint = [x, y];
24565         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24566     },
24567     
24568     /** 
24569      * @private Called after the drag operation by the DDProxy
24570      */
24571     onEndProxyDrag : function(e){
24572         Roo.get(this.proxy).setDisplayed(false);
24573         var endPoint = Roo.lib.Event.getXY(e);
24574         if(this.overlay){
24575             this.overlay.hide();
24576         }
24577         var newSize;
24578         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24579             newSize = this.dragSpecs.startSize + 
24580                 (this.placement == Roo.SplitBar.LEFT ?
24581                     endPoint[0] - this.dragSpecs.startPoint[0] :
24582                     this.dragSpecs.startPoint[0] - endPoint[0]
24583                 );
24584         }else{
24585             newSize = this.dragSpecs.startSize + 
24586                 (this.placement == Roo.SplitBar.TOP ?
24587                     endPoint[1] - this.dragSpecs.startPoint[1] :
24588                     this.dragSpecs.startPoint[1] - endPoint[1]
24589                 );
24590         }
24591         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24592         if(newSize != this.dragSpecs.startSize){
24593             if(this.fireEvent('beforeapply', this, newSize) !== false){
24594                 this.adapter.setElementSize(this, newSize);
24595                 this.fireEvent("moved", this, newSize);
24596                 this.fireEvent("resize", this, newSize);
24597             }
24598         }
24599     },
24600     
24601     /**
24602      * Get the adapter this SplitBar uses
24603      * @return The adapter object
24604      */
24605     getAdapter : function(){
24606         return this.adapter;
24607     },
24608     
24609     /**
24610      * Set the adapter this SplitBar uses
24611      * @param {Object} adapter A SplitBar adapter object
24612      */
24613     setAdapter : function(adapter){
24614         this.adapter = adapter;
24615         this.adapter.init(this);
24616     },
24617     
24618     /**
24619      * Gets the minimum size for the resizing element
24620      * @return {Number} The minimum size
24621      */
24622     getMinimumSize : function(){
24623         return this.minSize;
24624     },
24625     
24626     /**
24627      * Sets the minimum size for the resizing element
24628      * @param {Number} minSize The minimum size
24629      */
24630     setMinimumSize : function(minSize){
24631         this.minSize = minSize;
24632     },
24633     
24634     /**
24635      * Gets the maximum size for the resizing element
24636      * @return {Number} The maximum size
24637      */
24638     getMaximumSize : function(){
24639         return this.maxSize;
24640     },
24641     
24642     /**
24643      * Sets the maximum size for the resizing element
24644      * @param {Number} maxSize The maximum size
24645      */
24646     setMaximumSize : function(maxSize){
24647         this.maxSize = maxSize;
24648     },
24649     
24650     /**
24651      * Sets the initialize size for the resizing element
24652      * @param {Number} size The initial size
24653      */
24654     setCurrentSize : function(size){
24655         var oldAnimate = this.animate;
24656         this.animate = false;
24657         this.adapter.setElementSize(this, size);
24658         this.animate = oldAnimate;
24659     },
24660     
24661     /**
24662      * Destroy this splitbar. 
24663      * @param {Boolean} removeEl True to remove the element
24664      */
24665     destroy : function(removeEl){
24666         if(this.shim){
24667             this.shim.remove();
24668         }
24669         this.dd.unreg();
24670         this.proxy.parentNode.removeChild(this.proxy);
24671         if(removeEl){
24672             this.el.remove();
24673         }
24674     }
24675 });
24676
24677 /**
24678  * @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.
24679  */
24680 Roo.SplitBar.createProxy = function(dir){
24681     var proxy = new Roo.Element(document.createElement("div"));
24682     proxy.unselectable();
24683     var cls = 'x-splitbar-proxy';
24684     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24685     document.body.appendChild(proxy.dom);
24686     return proxy.dom;
24687 };
24688
24689 /** 
24690  * @class Roo.SplitBar.BasicLayoutAdapter
24691  * Default Adapter. It assumes the splitter and resizing element are not positioned
24692  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24693  */
24694 Roo.SplitBar.BasicLayoutAdapter = function(){
24695 };
24696
24697 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24698     // do nothing for now
24699     init : function(s){
24700     
24701     },
24702     /**
24703      * Called before drag operations to get the current size of the resizing element. 
24704      * @param {Roo.SplitBar} s The SplitBar using this adapter
24705      */
24706      getElementSize : function(s){
24707         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24708             return s.resizingEl.getWidth();
24709         }else{
24710             return s.resizingEl.getHeight();
24711         }
24712     },
24713     
24714     /**
24715      * Called after drag operations to set the size of the resizing element.
24716      * @param {Roo.SplitBar} s The SplitBar using this adapter
24717      * @param {Number} newSize The new size to set
24718      * @param {Function} onComplete A function to be invoked when resizing is complete
24719      */
24720     setElementSize : function(s, newSize, onComplete){
24721         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24722             if(!s.animate){
24723                 s.resizingEl.setWidth(newSize);
24724                 if(onComplete){
24725                     onComplete(s, newSize);
24726                 }
24727             }else{
24728                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24729             }
24730         }else{
24731             
24732             if(!s.animate){
24733                 s.resizingEl.setHeight(newSize);
24734                 if(onComplete){
24735                     onComplete(s, newSize);
24736                 }
24737             }else{
24738                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24739             }
24740         }
24741     }
24742 };
24743
24744 /** 
24745  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24746  * @extends Roo.SplitBar.BasicLayoutAdapter
24747  * Adapter that  moves the splitter element to align with the resized sizing element. 
24748  * Used with an absolute positioned SplitBar.
24749  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24750  * document.body, make sure you assign an id to the body element.
24751  */
24752 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24753     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24754     this.container = Roo.get(container);
24755 };
24756
24757 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24758     init : function(s){
24759         this.basic.init(s);
24760     },
24761     
24762     getElementSize : function(s){
24763         return this.basic.getElementSize(s);
24764     },
24765     
24766     setElementSize : function(s, newSize, onComplete){
24767         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24768     },
24769     
24770     moveSplitter : function(s){
24771         var yes = Roo.SplitBar;
24772         switch(s.placement){
24773             case yes.LEFT:
24774                 s.el.setX(s.resizingEl.getRight());
24775                 break;
24776             case yes.RIGHT:
24777                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24778                 break;
24779             case yes.TOP:
24780                 s.el.setY(s.resizingEl.getBottom());
24781                 break;
24782             case yes.BOTTOM:
24783                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24784                 break;
24785         }
24786     }
24787 };
24788
24789 /**
24790  * Orientation constant - Create a vertical SplitBar
24791  * @static
24792  * @type Number
24793  */
24794 Roo.SplitBar.VERTICAL = 1;
24795
24796 /**
24797  * Orientation constant - Create a horizontal SplitBar
24798  * @static
24799  * @type Number
24800  */
24801 Roo.SplitBar.HORIZONTAL = 2;
24802
24803 /**
24804  * Placement constant - The resizing element is to the left of the splitter element
24805  * @static
24806  * @type Number
24807  */
24808 Roo.SplitBar.LEFT = 1;
24809
24810 /**
24811  * Placement constant - The resizing element is to the right of the splitter element
24812  * @static
24813  * @type Number
24814  */
24815 Roo.SplitBar.RIGHT = 2;
24816
24817 /**
24818  * Placement constant - The resizing element is positioned above the splitter element
24819  * @static
24820  * @type Number
24821  */
24822 Roo.SplitBar.TOP = 3;
24823
24824 /**
24825  * Placement constant - The resizing element is positioned under splitter element
24826  * @static
24827  * @type Number
24828  */
24829 Roo.SplitBar.BOTTOM = 4;
24830 /*
24831  * Based on:
24832  * Ext JS Library 1.1.1
24833  * Copyright(c) 2006-2007, Ext JS, LLC.
24834  *
24835  * Originally Released Under LGPL - original licence link has changed is not relivant.
24836  *
24837  * Fork - LGPL
24838  * <script type="text/javascript">
24839  */
24840
24841 /**
24842  * @class Roo.View
24843  * @extends Roo.util.Observable
24844  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24845  * This class also supports single and multi selection modes. <br>
24846  * Create a data model bound view:
24847  <pre><code>
24848  var store = new Roo.data.Store(...);
24849
24850  var view = new Roo.View({
24851     el : "my-element",
24852     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24853  
24854     singleSelect: true,
24855     selectedClass: "ydataview-selected",
24856     store: store
24857  });
24858
24859  // listen for node click?
24860  view.on("click", function(vw, index, node, e){
24861  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24862  });
24863
24864  // load XML data
24865  dataModel.load("foobar.xml");
24866  </code></pre>
24867  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24868  * <br><br>
24869  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24870  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24871  * 
24872  * Note: old style constructor is still suported (container, template, config)
24873  * 
24874  * @constructor
24875  * Create a new View
24876  * @param {Object} config The config object
24877  * 
24878  */
24879 Roo.View = function(config, depreciated_tpl, depreciated_config){
24880     
24881     this.parent = false;
24882     
24883     if (typeof(depreciated_tpl) == 'undefined') {
24884         // new way.. - universal constructor.
24885         Roo.apply(this, config);
24886         this.el  = Roo.get(this.el);
24887     } else {
24888         // old format..
24889         this.el  = Roo.get(config);
24890         this.tpl = depreciated_tpl;
24891         Roo.apply(this, depreciated_config);
24892     }
24893     this.wrapEl  = this.el.wrap().wrap();
24894     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24895     
24896     
24897     if(typeof(this.tpl) == "string"){
24898         this.tpl = new Roo.Template(this.tpl);
24899     } else {
24900         // support xtype ctors..
24901         this.tpl = new Roo.factory(this.tpl, Roo);
24902     }
24903     
24904     
24905     this.tpl.compile();
24906     
24907     /** @private */
24908     this.addEvents({
24909         /**
24910          * @event beforeclick
24911          * Fires before a click is processed. Returns false to cancel the default action.
24912          * @param {Roo.View} this
24913          * @param {Number} index The index of the target node
24914          * @param {HTMLElement} node The target node
24915          * @param {Roo.EventObject} e The raw event object
24916          */
24917             "beforeclick" : true,
24918         /**
24919          * @event click
24920          * Fires when a template node is clicked.
24921          * @param {Roo.View} this
24922          * @param {Number} index The index of the target node
24923          * @param {HTMLElement} node The target node
24924          * @param {Roo.EventObject} e The raw event object
24925          */
24926             "click" : true,
24927         /**
24928          * @event dblclick
24929          * Fires when a template node is double clicked.
24930          * @param {Roo.View} this
24931          * @param {Number} index The index of the target node
24932          * @param {HTMLElement} node The target node
24933          * @param {Roo.EventObject} e The raw event object
24934          */
24935             "dblclick" : true,
24936         /**
24937          * @event contextmenu
24938          * Fires when a template node is right clicked.
24939          * @param {Roo.View} this
24940          * @param {Number} index The index of the target node
24941          * @param {HTMLElement} node The target node
24942          * @param {Roo.EventObject} e The raw event object
24943          */
24944             "contextmenu" : true,
24945         /**
24946          * @event selectionchange
24947          * Fires when the selected nodes change.
24948          * @param {Roo.View} this
24949          * @param {Array} selections Array of the selected nodes
24950          */
24951             "selectionchange" : true,
24952     
24953         /**
24954          * @event beforeselect
24955          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24956          * @param {Roo.View} this
24957          * @param {HTMLElement} node The node to be selected
24958          * @param {Array} selections Array of currently selected nodes
24959          */
24960             "beforeselect" : true,
24961         /**
24962          * @event preparedata
24963          * Fires on every row to render, to allow you to change the data.
24964          * @param {Roo.View} this
24965          * @param {Object} data to be rendered (change this)
24966          */
24967           "preparedata" : true
24968           
24969           
24970         });
24971
24972
24973
24974     this.el.on({
24975         "click": this.onClick,
24976         "dblclick": this.onDblClick,
24977         "contextmenu": this.onContextMenu,
24978         scope:this
24979     });
24980
24981     this.selections = [];
24982     this.nodes = [];
24983     this.cmp = new Roo.CompositeElementLite([]);
24984     if(this.store){
24985         this.store = Roo.factory(this.store, Roo.data);
24986         this.setStore(this.store, true);
24987     }
24988     
24989     if ( this.footer && this.footer.xtype) {
24990            
24991          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24992         
24993         this.footer.dataSource = this.store
24994         this.footer.container = fctr;
24995         this.footer = Roo.factory(this.footer, Roo);
24996         fctr.insertFirst(this.el);
24997         
24998         // this is a bit insane - as the paging toolbar seems to detach the el..
24999 //        dom.parentNode.parentNode.parentNode
25000          // they get detached?
25001     }
25002     
25003     
25004     Roo.View.superclass.constructor.call(this);
25005     
25006     
25007 };
25008
25009 Roo.extend(Roo.View, Roo.util.Observable, {
25010     
25011      /**
25012      * @cfg {Roo.data.Store} store Data store to load data from.
25013      */
25014     store : false,
25015     
25016     /**
25017      * @cfg {String|Roo.Element} el The container element.
25018      */
25019     el : '',
25020     
25021     /**
25022      * @cfg {String|Roo.Template} tpl The template used by this View 
25023      */
25024     tpl : false,
25025     /**
25026      * @cfg {String} dataName the named area of the template to use as the data area
25027      *                          Works with domtemplates roo-name="name"
25028      */
25029     dataName: false,
25030     /**
25031      * @cfg {String} selectedClass The css class to add to selected nodes
25032      */
25033     selectedClass : "x-view-selected",
25034      /**
25035      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25036      */
25037     emptyText : "",
25038     
25039     /**
25040      * @cfg {String} text to display on mask (default Loading)
25041      */
25042     mask : false,
25043     /**
25044      * @cfg {Boolean} multiSelect Allow multiple selection
25045      */
25046     multiSelect : false,
25047     /**
25048      * @cfg {Boolean} singleSelect Allow single selection
25049      */
25050     singleSelect:  false,
25051     
25052     /**
25053      * @cfg {Boolean} toggleSelect - selecting 
25054      */
25055     toggleSelect : false,
25056     
25057     /**
25058      * @cfg {Boolean} tickable - selecting 
25059      */
25060     tickable : false,
25061     
25062     /**
25063      * Returns the element this view is bound to.
25064      * @return {Roo.Element}
25065      */
25066     getEl : function(){
25067         return this.wrapEl;
25068     },
25069     
25070     
25071
25072     /**
25073      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25074      */
25075     refresh : function(){
25076         //Roo.log('refresh');
25077         var t = this.tpl;
25078         
25079         // if we are using something like 'domtemplate', then
25080         // the what gets used is:
25081         // t.applySubtemplate(NAME, data, wrapping data..)
25082         // the outer template then get' applied with
25083         //     the store 'extra data'
25084         // and the body get's added to the
25085         //      roo-name="data" node?
25086         //      <span class='roo-tpl-{name}'></span> ?????
25087         
25088         
25089         
25090         this.clearSelections();
25091         this.el.update("");
25092         var html = [];
25093         var records = this.store.getRange();
25094         if(records.length < 1) {
25095             
25096             // is this valid??  = should it render a template??
25097             
25098             this.el.update(this.emptyText);
25099             return;
25100         }
25101         var el = this.el;
25102         if (this.dataName) {
25103             this.el.update(t.apply(this.store.meta)); //????
25104             el = this.el.child('.roo-tpl-' + this.dataName);
25105         }
25106         
25107         for(var i = 0, len = records.length; i < len; i++){
25108             var data = this.prepareData(records[i].data, i, records[i]);
25109             this.fireEvent("preparedata", this, data, i, records[i]);
25110             
25111             var d = Roo.apply({}, data);
25112             
25113             if(this.tickable){
25114                 Roo.apply(d, {'roo-id' : Roo.id()});
25115                 
25116                 var _this = this;
25117             
25118                 Roo.each(this.parent.item, function(item){
25119                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25120                         return;
25121                     }
25122                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25123                 });
25124             }
25125             
25126             html[html.length] = Roo.util.Format.trim(
25127                 this.dataName ?
25128                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25129                     t.apply(d)
25130             );
25131         }
25132         
25133         
25134         
25135         el.update(html.join(""));
25136         this.nodes = el.dom.childNodes;
25137         this.updateIndexes(0);
25138     },
25139     
25140
25141     /**
25142      * Function to override to reformat the data that is sent to
25143      * the template for each node.
25144      * DEPRICATED - use the preparedata event handler.
25145      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25146      * a JSON object for an UpdateManager bound view).
25147      */
25148     prepareData : function(data, index, record)
25149     {
25150         this.fireEvent("preparedata", this, data, index, record);
25151         return data;
25152     },
25153
25154     onUpdate : function(ds, record){
25155         // Roo.log('on update');   
25156         this.clearSelections();
25157         var index = this.store.indexOf(record);
25158         var n = this.nodes[index];
25159         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25160         n.parentNode.removeChild(n);
25161         this.updateIndexes(index, index);
25162     },
25163
25164     
25165     
25166 // --------- FIXME     
25167     onAdd : function(ds, records, index)
25168     {
25169         //Roo.log(['on Add', ds, records, index] );        
25170         this.clearSelections();
25171         if(this.nodes.length == 0){
25172             this.refresh();
25173             return;
25174         }
25175         var n = this.nodes[index];
25176         for(var i = 0, len = records.length; i < len; i++){
25177             var d = this.prepareData(records[i].data, i, records[i]);
25178             if(n){
25179                 this.tpl.insertBefore(n, d);
25180             }else{
25181                 
25182                 this.tpl.append(this.el, d);
25183             }
25184         }
25185         this.updateIndexes(index);
25186     },
25187
25188     onRemove : function(ds, record, index){
25189        // Roo.log('onRemove');
25190         this.clearSelections();
25191         var el = this.dataName  ?
25192             this.el.child('.roo-tpl-' + this.dataName) :
25193             this.el; 
25194         
25195         el.dom.removeChild(this.nodes[index]);
25196         this.updateIndexes(index);
25197     },
25198
25199     /**
25200      * Refresh an individual node.
25201      * @param {Number} index
25202      */
25203     refreshNode : function(index){
25204         this.onUpdate(this.store, this.store.getAt(index));
25205     },
25206
25207     updateIndexes : function(startIndex, endIndex){
25208         var ns = this.nodes;
25209         startIndex = startIndex || 0;
25210         endIndex = endIndex || ns.length - 1;
25211         for(var i = startIndex; i <= endIndex; i++){
25212             ns[i].nodeIndex = i;
25213         }
25214     },
25215
25216     /**
25217      * Changes the data store this view uses and refresh the view.
25218      * @param {Store} store
25219      */
25220     setStore : function(store, initial){
25221         if(!initial && this.store){
25222             this.store.un("datachanged", this.refresh);
25223             this.store.un("add", this.onAdd);
25224             this.store.un("remove", this.onRemove);
25225             this.store.un("update", this.onUpdate);
25226             this.store.un("clear", this.refresh);
25227             this.store.un("beforeload", this.onBeforeLoad);
25228             this.store.un("load", this.onLoad);
25229             this.store.un("loadexception", this.onLoad);
25230         }
25231         if(store){
25232           
25233             store.on("datachanged", this.refresh, this);
25234             store.on("add", this.onAdd, this);
25235             store.on("remove", this.onRemove, this);
25236             store.on("update", this.onUpdate, this);
25237             store.on("clear", this.refresh, this);
25238             store.on("beforeload", this.onBeforeLoad, this);
25239             store.on("load", this.onLoad, this);
25240             store.on("loadexception", this.onLoad, this);
25241         }
25242         
25243         if(store){
25244             this.refresh();
25245         }
25246     },
25247     /**
25248      * onbeforeLoad - masks the loading area.
25249      *
25250      */
25251     onBeforeLoad : function(store,opts)
25252     {
25253          //Roo.log('onBeforeLoad');   
25254         if (!opts.add) {
25255             this.el.update("");
25256         }
25257         this.el.mask(this.mask ? this.mask : "Loading" ); 
25258     },
25259     onLoad : function ()
25260     {
25261         this.el.unmask();
25262     },
25263     
25264
25265     /**
25266      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25267      * @param {HTMLElement} node
25268      * @return {HTMLElement} The template node
25269      */
25270     findItemFromChild : function(node){
25271         var el = this.dataName  ?
25272             this.el.child('.roo-tpl-' + this.dataName,true) :
25273             this.el.dom; 
25274         
25275         if(!node || node.parentNode == el){
25276                     return node;
25277             }
25278             var p = node.parentNode;
25279             while(p && p != el){
25280             if(p.parentNode == el){
25281                 return p;
25282             }
25283             p = p.parentNode;
25284         }
25285             return null;
25286     },
25287
25288     /** @ignore */
25289     onClick : function(e){
25290         var item = this.findItemFromChild(e.getTarget());
25291         if(item){
25292             var index = this.indexOf(item);
25293             if(this.onItemClick(item, index, e) !== false){
25294                 this.fireEvent("click", this, index, item, e);
25295             }
25296         }else{
25297             this.clearSelections();
25298         }
25299     },
25300
25301     /** @ignore */
25302     onContextMenu : function(e){
25303         var item = this.findItemFromChild(e.getTarget());
25304         if(item){
25305             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25306         }
25307     },
25308
25309     /** @ignore */
25310     onDblClick : function(e){
25311         var item = this.findItemFromChild(e.getTarget());
25312         if(item){
25313             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25314         }
25315     },
25316
25317     onItemClick : function(item, index, e)
25318     {
25319         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25320             return false;
25321         }
25322         if (this.toggleSelect) {
25323             var m = this.isSelected(item) ? 'unselect' : 'select';
25324             //Roo.log(m);
25325             var _t = this;
25326             _t[m](item, true, false);
25327             return true;
25328         }
25329         if(this.multiSelect || this.singleSelect){
25330             if(this.multiSelect && e.shiftKey && this.lastSelection){
25331                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25332             }else{
25333                 this.select(item, this.multiSelect && e.ctrlKey);
25334                 this.lastSelection = item;
25335             }
25336             
25337             if(!this.tickable){
25338                 e.preventDefault();
25339             }
25340             
25341         }
25342         return true;
25343     },
25344
25345     /**
25346      * Get the number of selected nodes.
25347      * @return {Number}
25348      */
25349     getSelectionCount : function(){
25350         return this.selections.length;
25351     },
25352
25353     /**
25354      * Get the currently selected nodes.
25355      * @return {Array} An array of HTMLElements
25356      */
25357     getSelectedNodes : function(){
25358         return this.selections;
25359     },
25360
25361     /**
25362      * Get the indexes of the selected nodes.
25363      * @return {Array}
25364      */
25365     getSelectedIndexes : function(){
25366         var indexes = [], s = this.selections;
25367         for(var i = 0, len = s.length; i < len; i++){
25368             indexes.push(s[i].nodeIndex);
25369         }
25370         return indexes;
25371     },
25372
25373     /**
25374      * Clear all selections
25375      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25376      */
25377     clearSelections : function(suppressEvent){
25378         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25379             this.cmp.elements = this.selections;
25380             this.cmp.removeClass(this.selectedClass);
25381             this.selections = [];
25382             if(!suppressEvent){
25383                 this.fireEvent("selectionchange", this, this.selections);
25384             }
25385         }
25386     },
25387
25388     /**
25389      * Returns true if the passed node is selected
25390      * @param {HTMLElement/Number} node The node or node index
25391      * @return {Boolean}
25392      */
25393     isSelected : function(node){
25394         var s = this.selections;
25395         if(s.length < 1){
25396             return false;
25397         }
25398         node = this.getNode(node);
25399         return s.indexOf(node) !== -1;
25400     },
25401
25402     /**
25403      * Selects nodes.
25404      * @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
25405      * @param {Boolean} keepExisting (optional) true to keep existing selections
25406      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25407      */
25408     select : function(nodeInfo, keepExisting, suppressEvent){
25409         if(nodeInfo instanceof Array){
25410             if(!keepExisting){
25411                 this.clearSelections(true);
25412             }
25413             for(var i = 0, len = nodeInfo.length; i < len; i++){
25414                 this.select(nodeInfo[i], true, true);
25415             }
25416             return;
25417         } 
25418         var node = this.getNode(nodeInfo);
25419         if(!node || this.isSelected(node)){
25420             return; // already selected.
25421         }
25422         if(!keepExisting){
25423             this.clearSelections(true);
25424         }
25425         
25426         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25427             Roo.fly(node).addClass(this.selectedClass);
25428             this.selections.push(node);
25429             if(!suppressEvent){
25430                 this.fireEvent("selectionchange", this, this.selections);
25431             }
25432         }
25433         
25434         
25435     },
25436       /**
25437      * Unselects nodes.
25438      * @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
25439      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25440      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25441      */
25442     unselect : function(nodeInfo, keepExisting, suppressEvent)
25443     {
25444         if(nodeInfo instanceof Array){
25445             Roo.each(this.selections, function(s) {
25446                 this.unselect(s, nodeInfo);
25447             }, this);
25448             return;
25449         }
25450         var node = this.getNode(nodeInfo);
25451         if(!node || !this.isSelected(node)){
25452             //Roo.log("not selected");
25453             return; // not selected.
25454         }
25455         // fireevent???
25456         var ns = [];
25457         Roo.each(this.selections, function(s) {
25458             if (s == node ) {
25459                 Roo.fly(node).removeClass(this.selectedClass);
25460
25461                 return;
25462             }
25463             ns.push(s);
25464         },this);
25465         
25466         this.selections= ns;
25467         this.fireEvent("selectionchange", this, this.selections);
25468     },
25469
25470     /**
25471      * Gets a template node.
25472      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25473      * @return {HTMLElement} The node or null if it wasn't found
25474      */
25475     getNode : function(nodeInfo){
25476         if(typeof nodeInfo == "string"){
25477             return document.getElementById(nodeInfo);
25478         }else if(typeof nodeInfo == "number"){
25479             return this.nodes[nodeInfo];
25480         }
25481         return nodeInfo;
25482     },
25483
25484     /**
25485      * Gets a range template nodes.
25486      * @param {Number} startIndex
25487      * @param {Number} endIndex
25488      * @return {Array} An array of nodes
25489      */
25490     getNodes : function(start, end){
25491         var ns = this.nodes;
25492         start = start || 0;
25493         end = typeof end == "undefined" ? ns.length - 1 : end;
25494         var nodes = [];
25495         if(start <= end){
25496             for(var i = start; i <= end; i++){
25497                 nodes.push(ns[i]);
25498             }
25499         } else{
25500             for(var i = start; i >= end; i--){
25501                 nodes.push(ns[i]);
25502             }
25503         }
25504         return nodes;
25505     },
25506
25507     /**
25508      * Finds the index of the passed node
25509      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25510      * @return {Number} The index of the node or -1
25511      */
25512     indexOf : function(node){
25513         node = this.getNode(node);
25514         if(typeof node.nodeIndex == "number"){
25515             return node.nodeIndex;
25516         }
25517         var ns = this.nodes;
25518         for(var i = 0, len = ns.length; i < len; i++){
25519             if(ns[i] == node){
25520                 return i;
25521             }
25522         }
25523         return -1;
25524     }
25525 });
25526 /*
25527  * Based on:
25528  * Ext JS Library 1.1.1
25529  * Copyright(c) 2006-2007, Ext JS, LLC.
25530  *
25531  * Originally Released Under LGPL - original licence link has changed is not relivant.
25532  *
25533  * Fork - LGPL
25534  * <script type="text/javascript">
25535  */
25536
25537 /**
25538  * @class Roo.JsonView
25539  * @extends Roo.View
25540  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25541 <pre><code>
25542 var view = new Roo.JsonView({
25543     container: "my-element",
25544     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25545     multiSelect: true, 
25546     jsonRoot: "data" 
25547 });
25548
25549 // listen for node click?
25550 view.on("click", function(vw, index, node, e){
25551     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25552 });
25553
25554 // direct load of JSON data
25555 view.load("foobar.php");
25556
25557 // Example from my blog list
25558 var tpl = new Roo.Template(
25559     '&lt;div class="entry"&gt;' +
25560     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25561     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25562     "&lt;/div&gt;&lt;hr /&gt;"
25563 );
25564
25565 var moreView = new Roo.JsonView({
25566     container :  "entry-list", 
25567     template : tpl,
25568     jsonRoot: "posts"
25569 });
25570 moreView.on("beforerender", this.sortEntries, this);
25571 moreView.load({
25572     url: "/blog/get-posts.php",
25573     params: "allposts=true",
25574     text: "Loading Blog Entries..."
25575 });
25576 </code></pre>
25577
25578 * Note: old code is supported with arguments : (container, template, config)
25579
25580
25581  * @constructor
25582  * Create a new JsonView
25583  * 
25584  * @param {Object} config The config object
25585  * 
25586  */
25587 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25588     
25589     
25590     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25591
25592     var um = this.el.getUpdateManager();
25593     um.setRenderer(this);
25594     um.on("update", this.onLoad, this);
25595     um.on("failure", this.onLoadException, this);
25596
25597     /**
25598      * @event beforerender
25599      * Fires before rendering of the downloaded JSON data.
25600      * @param {Roo.JsonView} this
25601      * @param {Object} data The JSON data loaded
25602      */
25603     /**
25604      * @event load
25605      * Fires when data is loaded.
25606      * @param {Roo.JsonView} this
25607      * @param {Object} data The JSON data loaded
25608      * @param {Object} response The raw Connect response object
25609      */
25610     /**
25611      * @event loadexception
25612      * Fires when loading fails.
25613      * @param {Roo.JsonView} this
25614      * @param {Object} response The raw Connect response object
25615      */
25616     this.addEvents({
25617         'beforerender' : true,
25618         'load' : true,
25619         'loadexception' : true
25620     });
25621 };
25622 Roo.extend(Roo.JsonView, Roo.View, {
25623     /**
25624      * @type {String} The root property in the loaded JSON object that contains the data
25625      */
25626     jsonRoot : "",
25627
25628     /**
25629      * Refreshes the view.
25630      */
25631     refresh : function(){
25632         this.clearSelections();
25633         this.el.update("");
25634         var html = [];
25635         var o = this.jsonData;
25636         if(o && o.length > 0){
25637             for(var i = 0, len = o.length; i < len; i++){
25638                 var data = this.prepareData(o[i], i, o);
25639                 html[html.length] = this.tpl.apply(data);
25640             }
25641         }else{
25642             html.push(this.emptyText);
25643         }
25644         this.el.update(html.join(""));
25645         this.nodes = this.el.dom.childNodes;
25646         this.updateIndexes(0);
25647     },
25648
25649     /**
25650      * 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.
25651      * @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:
25652      <pre><code>
25653      view.load({
25654          url: "your-url.php",
25655          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25656          callback: yourFunction,
25657          scope: yourObject, //(optional scope)
25658          discardUrl: false,
25659          nocache: false,
25660          text: "Loading...",
25661          timeout: 30,
25662          scripts: false
25663      });
25664      </code></pre>
25665      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25666      * 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.
25667      * @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}
25668      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25669      * @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.
25670      */
25671     load : function(){
25672         var um = this.el.getUpdateManager();
25673         um.update.apply(um, arguments);
25674     },
25675
25676     render : function(el, response){
25677         this.clearSelections();
25678         this.el.update("");
25679         var o;
25680         try{
25681             o = Roo.util.JSON.decode(response.responseText);
25682             if(this.jsonRoot){
25683                 
25684                 o = o[this.jsonRoot];
25685             }
25686         } catch(e){
25687         }
25688         /**
25689          * The current JSON data or null
25690          */
25691         this.jsonData = o;
25692         this.beforeRender();
25693         this.refresh();
25694     },
25695
25696 /**
25697  * Get the number of records in the current JSON dataset
25698  * @return {Number}
25699  */
25700     getCount : function(){
25701         return this.jsonData ? this.jsonData.length : 0;
25702     },
25703
25704 /**
25705  * Returns the JSON object for the specified node(s)
25706  * @param {HTMLElement/Array} node The node or an array of nodes
25707  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25708  * you get the JSON object for the node
25709  */
25710     getNodeData : function(node){
25711         if(node instanceof Array){
25712             var data = [];
25713             for(var i = 0, len = node.length; i < len; i++){
25714                 data.push(this.getNodeData(node[i]));
25715             }
25716             return data;
25717         }
25718         return this.jsonData[this.indexOf(node)] || null;
25719     },
25720
25721     beforeRender : function(){
25722         this.snapshot = this.jsonData;
25723         if(this.sortInfo){
25724             this.sort.apply(this, this.sortInfo);
25725         }
25726         this.fireEvent("beforerender", this, this.jsonData);
25727     },
25728
25729     onLoad : function(el, o){
25730         this.fireEvent("load", this, this.jsonData, o);
25731     },
25732
25733     onLoadException : function(el, o){
25734         this.fireEvent("loadexception", this, o);
25735     },
25736
25737 /**
25738  * Filter the data by a specific property.
25739  * @param {String} property A property on your JSON objects
25740  * @param {String/RegExp} value Either string that the property values
25741  * should start with, or a RegExp to test against the property
25742  */
25743     filter : function(property, value){
25744         if(this.jsonData){
25745             var data = [];
25746             var ss = this.snapshot;
25747             if(typeof value == "string"){
25748                 var vlen = value.length;
25749                 if(vlen == 0){
25750                     this.clearFilter();
25751                     return;
25752                 }
25753                 value = value.toLowerCase();
25754                 for(var i = 0, len = ss.length; i < len; i++){
25755                     var o = ss[i];
25756                     if(o[property].substr(0, vlen).toLowerCase() == value){
25757                         data.push(o);
25758                     }
25759                 }
25760             } else if(value.exec){ // regex?
25761                 for(var i = 0, len = ss.length; i < len; i++){
25762                     var o = ss[i];
25763                     if(value.test(o[property])){
25764                         data.push(o);
25765                     }
25766                 }
25767             } else{
25768                 return;
25769             }
25770             this.jsonData = data;
25771             this.refresh();
25772         }
25773     },
25774
25775 /**
25776  * Filter by a function. The passed function will be called with each
25777  * object in the current dataset. If the function returns true the value is kept,
25778  * otherwise it is filtered.
25779  * @param {Function} fn
25780  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25781  */
25782     filterBy : function(fn, scope){
25783         if(this.jsonData){
25784             var data = [];
25785             var ss = this.snapshot;
25786             for(var i = 0, len = ss.length; i < len; i++){
25787                 var o = ss[i];
25788                 if(fn.call(scope || this, o)){
25789                     data.push(o);
25790                 }
25791             }
25792             this.jsonData = data;
25793             this.refresh();
25794         }
25795     },
25796
25797 /**
25798  * Clears the current filter.
25799  */
25800     clearFilter : function(){
25801         if(this.snapshot && this.jsonData != this.snapshot){
25802             this.jsonData = this.snapshot;
25803             this.refresh();
25804         }
25805     },
25806
25807
25808 /**
25809  * Sorts the data for this view and refreshes it.
25810  * @param {String} property A property on your JSON objects to sort on
25811  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25812  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25813  */
25814     sort : function(property, dir, sortType){
25815         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25816         if(this.jsonData){
25817             var p = property;
25818             var dsc = dir && dir.toLowerCase() == "desc";
25819             var f = function(o1, o2){
25820                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25821                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25822                 ;
25823                 if(v1 < v2){
25824                     return dsc ? +1 : -1;
25825                 } else if(v1 > v2){
25826                     return dsc ? -1 : +1;
25827                 } else{
25828                     return 0;
25829                 }
25830             };
25831             this.jsonData.sort(f);
25832             this.refresh();
25833             if(this.jsonData != this.snapshot){
25834                 this.snapshot.sort(f);
25835             }
25836         }
25837     }
25838 });/*
25839  * Based on:
25840  * Ext JS Library 1.1.1
25841  * Copyright(c) 2006-2007, Ext JS, LLC.
25842  *
25843  * Originally Released Under LGPL - original licence link has changed is not relivant.
25844  *
25845  * Fork - LGPL
25846  * <script type="text/javascript">
25847  */
25848  
25849
25850 /**
25851  * @class Roo.ColorPalette
25852  * @extends Roo.Component
25853  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25854  * Here's an example of typical usage:
25855  * <pre><code>
25856 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25857 cp.render('my-div');
25858
25859 cp.on('select', function(palette, selColor){
25860     // do something with selColor
25861 });
25862 </code></pre>
25863  * @constructor
25864  * Create a new ColorPalette
25865  * @param {Object} config The config object
25866  */
25867 Roo.ColorPalette = function(config){
25868     Roo.ColorPalette.superclass.constructor.call(this, config);
25869     this.addEvents({
25870         /**
25871              * @event select
25872              * Fires when a color is selected
25873              * @param {ColorPalette} this
25874              * @param {String} color The 6-digit color hex code (without the # symbol)
25875              */
25876         select: true
25877     });
25878
25879     if(this.handler){
25880         this.on("select", this.handler, this.scope, true);
25881     }
25882 };
25883 Roo.extend(Roo.ColorPalette, Roo.Component, {
25884     /**
25885      * @cfg {String} itemCls
25886      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25887      */
25888     itemCls : "x-color-palette",
25889     /**
25890      * @cfg {String} value
25891      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25892      * the hex codes are case-sensitive.
25893      */
25894     value : null,
25895     clickEvent:'click',
25896     // private
25897     ctype: "Roo.ColorPalette",
25898
25899     /**
25900      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25901      */
25902     allowReselect : false,
25903
25904     /**
25905      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25906      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25907      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25908      * of colors with the width setting until the box is symmetrical.</p>
25909      * <p>You can override individual colors if needed:</p>
25910      * <pre><code>
25911 var cp = new Roo.ColorPalette();
25912 cp.colors[0] = "FF0000";  // change the first box to red
25913 </code></pre>
25914
25915 Or you can provide a custom array of your own for complete control:
25916 <pre><code>
25917 var cp = new Roo.ColorPalette();
25918 cp.colors = ["000000", "993300", "333300"];
25919 </code></pre>
25920      * @type Array
25921      */
25922     colors : [
25923         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25924         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25925         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25926         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25927         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25928     ],
25929
25930     // private
25931     onRender : function(container, position){
25932         var t = new Roo.MasterTemplate(
25933             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25934         );
25935         var c = this.colors;
25936         for(var i = 0, len = c.length; i < len; i++){
25937             t.add([c[i]]);
25938         }
25939         var el = document.createElement("div");
25940         el.className = this.itemCls;
25941         t.overwrite(el);
25942         container.dom.insertBefore(el, position);
25943         this.el = Roo.get(el);
25944         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25945         if(this.clickEvent != 'click'){
25946             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25947         }
25948     },
25949
25950     // private
25951     afterRender : function(){
25952         Roo.ColorPalette.superclass.afterRender.call(this);
25953         if(this.value){
25954             var s = this.value;
25955             this.value = null;
25956             this.select(s);
25957         }
25958     },
25959
25960     // private
25961     handleClick : function(e, t){
25962         e.preventDefault();
25963         if(!this.disabled){
25964             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25965             this.select(c.toUpperCase());
25966         }
25967     },
25968
25969     /**
25970      * Selects the specified color in the palette (fires the select event)
25971      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25972      */
25973     select : function(color){
25974         color = color.replace("#", "");
25975         if(color != this.value || this.allowReselect){
25976             var el = this.el;
25977             if(this.value){
25978                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25979             }
25980             el.child("a.color-"+color).addClass("x-color-palette-sel");
25981             this.value = color;
25982             this.fireEvent("select", this, color);
25983         }
25984     }
25985 });/*
25986  * Based on:
25987  * Ext JS Library 1.1.1
25988  * Copyright(c) 2006-2007, Ext JS, LLC.
25989  *
25990  * Originally Released Under LGPL - original licence link has changed is not relivant.
25991  *
25992  * Fork - LGPL
25993  * <script type="text/javascript">
25994  */
25995  
25996 /**
25997  * @class Roo.DatePicker
25998  * @extends Roo.Component
25999  * Simple date picker class.
26000  * @constructor
26001  * Create a new DatePicker
26002  * @param {Object} config The config object
26003  */
26004 Roo.DatePicker = function(config){
26005     Roo.DatePicker.superclass.constructor.call(this, config);
26006
26007     this.value = config && config.value ?
26008                  config.value.clearTime() : new Date().clearTime();
26009
26010     this.addEvents({
26011         /**
26012              * @event select
26013              * Fires when a date is selected
26014              * @param {DatePicker} this
26015              * @param {Date} date The selected date
26016              */
26017         'select': true,
26018         /**
26019              * @event monthchange
26020              * Fires when the displayed month changes 
26021              * @param {DatePicker} this
26022              * @param {Date} date The selected month
26023              */
26024         'monthchange': true
26025     });
26026
26027     if(this.handler){
26028         this.on("select", this.handler,  this.scope || this);
26029     }
26030     // build the disabledDatesRE
26031     if(!this.disabledDatesRE && this.disabledDates){
26032         var dd = this.disabledDates;
26033         var re = "(?:";
26034         for(var i = 0; i < dd.length; i++){
26035             re += dd[i];
26036             if(i != dd.length-1) re += "|";
26037         }
26038         this.disabledDatesRE = new RegExp(re + ")");
26039     }
26040 };
26041
26042 Roo.extend(Roo.DatePicker, Roo.Component, {
26043     /**
26044      * @cfg {String} todayText
26045      * The text to display on the button that selects the current date (defaults to "Today")
26046      */
26047     todayText : "Today",
26048     /**
26049      * @cfg {String} okText
26050      * The text to display on the ok button
26051      */
26052     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26053     /**
26054      * @cfg {String} cancelText
26055      * The text to display on the cancel button
26056      */
26057     cancelText : "Cancel",
26058     /**
26059      * @cfg {String} todayTip
26060      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26061      */
26062     todayTip : "{0} (Spacebar)",
26063     /**
26064      * @cfg {Date} minDate
26065      * Minimum allowable date (JavaScript date object, defaults to null)
26066      */
26067     minDate : null,
26068     /**
26069      * @cfg {Date} maxDate
26070      * Maximum allowable date (JavaScript date object, defaults to null)
26071      */
26072     maxDate : null,
26073     /**
26074      * @cfg {String} minText
26075      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26076      */
26077     minText : "This date is before the minimum date",
26078     /**
26079      * @cfg {String} maxText
26080      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26081      */
26082     maxText : "This date is after the maximum date",
26083     /**
26084      * @cfg {String} format
26085      * The default date format string which can be overriden for localization support.  The format must be
26086      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26087      */
26088     format : "m/d/y",
26089     /**
26090      * @cfg {Array} disabledDays
26091      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26092      */
26093     disabledDays : null,
26094     /**
26095      * @cfg {String} disabledDaysText
26096      * The tooltip to display when the date falls on a disabled day (defaults to "")
26097      */
26098     disabledDaysText : "",
26099     /**
26100      * @cfg {RegExp} disabledDatesRE
26101      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26102      */
26103     disabledDatesRE : null,
26104     /**
26105      * @cfg {String} disabledDatesText
26106      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26107      */
26108     disabledDatesText : "",
26109     /**
26110      * @cfg {Boolean} constrainToViewport
26111      * True to constrain the date picker to the viewport (defaults to true)
26112      */
26113     constrainToViewport : true,
26114     /**
26115      * @cfg {Array} monthNames
26116      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26117      */
26118     monthNames : Date.monthNames,
26119     /**
26120      * @cfg {Array} dayNames
26121      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26122      */
26123     dayNames : Date.dayNames,
26124     /**
26125      * @cfg {String} nextText
26126      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26127      */
26128     nextText: 'Next Month (Control+Right)',
26129     /**
26130      * @cfg {String} prevText
26131      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26132      */
26133     prevText: 'Previous Month (Control+Left)',
26134     /**
26135      * @cfg {String} monthYearText
26136      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26137      */
26138     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26139     /**
26140      * @cfg {Number} startDay
26141      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26142      */
26143     startDay : 0,
26144     /**
26145      * @cfg {Bool} showClear
26146      * Show a clear button (usefull for date form elements that can be blank.)
26147      */
26148     
26149     showClear: false,
26150     
26151     /**
26152      * Sets the value of the date field
26153      * @param {Date} value The date to set
26154      */
26155     setValue : function(value){
26156         var old = this.value;
26157         
26158         if (typeof(value) == 'string') {
26159          
26160             value = Date.parseDate(value, this.format);
26161         }
26162         if (!value) {
26163             value = new Date();
26164         }
26165         
26166         this.value = value.clearTime(true);
26167         if(this.el){
26168             this.update(this.value);
26169         }
26170     },
26171
26172     /**
26173      * Gets the current selected value of the date field
26174      * @return {Date} The selected date
26175      */
26176     getValue : function(){
26177         return this.value;
26178     },
26179
26180     // private
26181     focus : function(){
26182         if(this.el){
26183             this.update(this.activeDate);
26184         }
26185     },
26186
26187     // privateval
26188     onRender : function(container, position){
26189         
26190         var m = [
26191              '<table cellspacing="0">',
26192                 '<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>',
26193                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26194         var dn = this.dayNames;
26195         for(var i = 0; i < 7; i++){
26196             var d = this.startDay+i;
26197             if(d > 6){
26198                 d = d-7;
26199             }
26200             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26201         }
26202         m[m.length] = "</tr></thead><tbody><tr>";
26203         for(var i = 0; i < 42; i++) {
26204             if(i % 7 == 0 && i != 0){
26205                 m[m.length] = "</tr><tr>";
26206             }
26207             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26208         }
26209         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26210             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26211
26212         var el = document.createElement("div");
26213         el.className = "x-date-picker";
26214         el.innerHTML = m.join("");
26215
26216         container.dom.insertBefore(el, position);
26217
26218         this.el = Roo.get(el);
26219         this.eventEl = Roo.get(el.firstChild);
26220
26221         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26222             handler: this.showPrevMonth,
26223             scope: this,
26224             preventDefault:true,
26225             stopDefault:true
26226         });
26227
26228         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26229             handler: this.showNextMonth,
26230             scope: this,
26231             preventDefault:true,
26232             stopDefault:true
26233         });
26234
26235         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26236
26237         this.monthPicker = this.el.down('div.x-date-mp');
26238         this.monthPicker.enableDisplayMode('block');
26239         
26240         var kn = new Roo.KeyNav(this.eventEl, {
26241             "left" : function(e){
26242                 e.ctrlKey ?
26243                     this.showPrevMonth() :
26244                     this.update(this.activeDate.add("d", -1));
26245             },
26246
26247             "right" : function(e){
26248                 e.ctrlKey ?
26249                     this.showNextMonth() :
26250                     this.update(this.activeDate.add("d", 1));
26251             },
26252
26253             "up" : function(e){
26254                 e.ctrlKey ?
26255                     this.showNextYear() :
26256                     this.update(this.activeDate.add("d", -7));
26257             },
26258
26259             "down" : function(e){
26260                 e.ctrlKey ?
26261                     this.showPrevYear() :
26262                     this.update(this.activeDate.add("d", 7));
26263             },
26264
26265             "pageUp" : function(e){
26266                 this.showNextMonth();
26267             },
26268
26269             "pageDown" : function(e){
26270                 this.showPrevMonth();
26271             },
26272
26273             "enter" : function(e){
26274                 e.stopPropagation();
26275                 return true;
26276             },
26277
26278             scope : this
26279         });
26280
26281         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26282
26283         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26284
26285         this.el.unselectable();
26286         
26287         this.cells = this.el.select("table.x-date-inner tbody td");
26288         this.textNodes = this.el.query("table.x-date-inner tbody span");
26289
26290         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26291             text: "&#160;",
26292             tooltip: this.monthYearText
26293         });
26294
26295         this.mbtn.on('click', this.showMonthPicker, this);
26296         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26297
26298
26299         var today = (new Date()).dateFormat(this.format);
26300         
26301         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26302         if (this.showClear) {
26303             baseTb.add( new Roo.Toolbar.Fill());
26304         }
26305         baseTb.add({
26306             text: String.format(this.todayText, today),
26307             tooltip: String.format(this.todayTip, today),
26308             handler: this.selectToday,
26309             scope: this
26310         });
26311         
26312         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26313             
26314         //});
26315         if (this.showClear) {
26316             
26317             baseTb.add( new Roo.Toolbar.Fill());
26318             baseTb.add({
26319                 text: '&#160;',
26320                 cls: 'x-btn-icon x-btn-clear',
26321                 handler: function() {
26322                     //this.value = '';
26323                     this.fireEvent("select", this, '');
26324                 },
26325                 scope: this
26326             });
26327         }
26328         
26329         
26330         if(Roo.isIE){
26331             this.el.repaint();
26332         }
26333         this.update(this.value);
26334     },
26335
26336     createMonthPicker : function(){
26337         if(!this.monthPicker.dom.firstChild){
26338             var buf = ['<table border="0" cellspacing="0">'];
26339             for(var i = 0; i < 6; i++){
26340                 buf.push(
26341                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26342                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26343                     i == 0 ?
26344                     '<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>' :
26345                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26346                 );
26347             }
26348             buf.push(
26349                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26350                     this.okText,
26351                     '</button><button type="button" class="x-date-mp-cancel">',
26352                     this.cancelText,
26353                     '</button></td></tr>',
26354                 '</table>'
26355             );
26356             this.monthPicker.update(buf.join(''));
26357             this.monthPicker.on('click', this.onMonthClick, this);
26358             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26359
26360             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26361             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26362
26363             this.mpMonths.each(function(m, a, i){
26364                 i += 1;
26365                 if((i%2) == 0){
26366                     m.dom.xmonth = 5 + Math.round(i * .5);
26367                 }else{
26368                     m.dom.xmonth = Math.round((i-1) * .5);
26369                 }
26370             });
26371         }
26372     },
26373
26374     showMonthPicker : function(){
26375         this.createMonthPicker();
26376         var size = this.el.getSize();
26377         this.monthPicker.setSize(size);
26378         this.monthPicker.child('table').setSize(size);
26379
26380         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26381         this.updateMPMonth(this.mpSelMonth);
26382         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26383         this.updateMPYear(this.mpSelYear);
26384
26385         this.monthPicker.slideIn('t', {duration:.2});
26386     },
26387
26388     updateMPYear : function(y){
26389         this.mpyear = y;
26390         var ys = this.mpYears.elements;
26391         for(var i = 1; i <= 10; i++){
26392             var td = ys[i-1], y2;
26393             if((i%2) == 0){
26394                 y2 = y + Math.round(i * .5);
26395                 td.firstChild.innerHTML = y2;
26396                 td.xyear = y2;
26397             }else{
26398                 y2 = y - (5-Math.round(i * .5));
26399                 td.firstChild.innerHTML = y2;
26400                 td.xyear = y2;
26401             }
26402             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26403         }
26404     },
26405
26406     updateMPMonth : function(sm){
26407         this.mpMonths.each(function(m, a, i){
26408             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26409         });
26410     },
26411
26412     selectMPMonth: function(m){
26413         
26414     },
26415
26416     onMonthClick : function(e, t){
26417         e.stopEvent();
26418         var el = new Roo.Element(t), pn;
26419         if(el.is('button.x-date-mp-cancel')){
26420             this.hideMonthPicker();
26421         }
26422         else if(el.is('button.x-date-mp-ok')){
26423             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26424             this.hideMonthPicker();
26425         }
26426         else if(pn = el.up('td.x-date-mp-month', 2)){
26427             this.mpMonths.removeClass('x-date-mp-sel');
26428             pn.addClass('x-date-mp-sel');
26429             this.mpSelMonth = pn.dom.xmonth;
26430         }
26431         else if(pn = el.up('td.x-date-mp-year', 2)){
26432             this.mpYears.removeClass('x-date-mp-sel');
26433             pn.addClass('x-date-mp-sel');
26434             this.mpSelYear = pn.dom.xyear;
26435         }
26436         else if(el.is('a.x-date-mp-prev')){
26437             this.updateMPYear(this.mpyear-10);
26438         }
26439         else if(el.is('a.x-date-mp-next')){
26440             this.updateMPYear(this.mpyear+10);
26441         }
26442     },
26443
26444     onMonthDblClick : function(e, t){
26445         e.stopEvent();
26446         var el = new Roo.Element(t), pn;
26447         if(pn = el.up('td.x-date-mp-month', 2)){
26448             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26449             this.hideMonthPicker();
26450         }
26451         else if(pn = el.up('td.x-date-mp-year', 2)){
26452             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26453             this.hideMonthPicker();
26454         }
26455     },
26456
26457     hideMonthPicker : function(disableAnim){
26458         if(this.monthPicker){
26459             if(disableAnim === true){
26460                 this.monthPicker.hide();
26461             }else{
26462                 this.monthPicker.slideOut('t', {duration:.2});
26463             }
26464         }
26465     },
26466
26467     // private
26468     showPrevMonth : function(e){
26469         this.update(this.activeDate.add("mo", -1));
26470     },
26471
26472     // private
26473     showNextMonth : function(e){
26474         this.update(this.activeDate.add("mo", 1));
26475     },
26476
26477     // private
26478     showPrevYear : function(){
26479         this.update(this.activeDate.add("y", -1));
26480     },
26481
26482     // private
26483     showNextYear : function(){
26484         this.update(this.activeDate.add("y", 1));
26485     },
26486
26487     // private
26488     handleMouseWheel : function(e){
26489         var delta = e.getWheelDelta();
26490         if(delta > 0){
26491             this.showPrevMonth();
26492             e.stopEvent();
26493         } else if(delta < 0){
26494             this.showNextMonth();
26495             e.stopEvent();
26496         }
26497     },
26498
26499     // private
26500     handleDateClick : function(e, t){
26501         e.stopEvent();
26502         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26503             this.setValue(new Date(t.dateValue));
26504             this.fireEvent("select", this, this.value);
26505         }
26506     },
26507
26508     // private
26509     selectToday : function(){
26510         this.setValue(new Date().clearTime());
26511         this.fireEvent("select", this, this.value);
26512     },
26513
26514     // private
26515     update : function(date)
26516     {
26517         var vd = this.activeDate;
26518         this.activeDate = date;
26519         if(vd && this.el){
26520             var t = date.getTime();
26521             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26522                 this.cells.removeClass("x-date-selected");
26523                 this.cells.each(function(c){
26524                    if(c.dom.firstChild.dateValue == t){
26525                        c.addClass("x-date-selected");
26526                        setTimeout(function(){
26527                             try{c.dom.firstChild.focus();}catch(e){}
26528                        }, 50);
26529                        return false;
26530                    }
26531                 });
26532                 return;
26533             }
26534         }
26535         
26536         var days = date.getDaysInMonth();
26537         var firstOfMonth = date.getFirstDateOfMonth();
26538         var startingPos = firstOfMonth.getDay()-this.startDay;
26539
26540         if(startingPos <= this.startDay){
26541             startingPos += 7;
26542         }
26543
26544         var pm = date.add("mo", -1);
26545         var prevStart = pm.getDaysInMonth()-startingPos;
26546
26547         var cells = this.cells.elements;
26548         var textEls = this.textNodes;
26549         days += startingPos;
26550
26551         // convert everything to numbers so it's fast
26552         var day = 86400000;
26553         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26554         var today = new Date().clearTime().getTime();
26555         var sel = date.clearTime().getTime();
26556         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26557         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26558         var ddMatch = this.disabledDatesRE;
26559         var ddText = this.disabledDatesText;
26560         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26561         var ddaysText = this.disabledDaysText;
26562         var format = this.format;
26563
26564         var setCellClass = function(cal, cell){
26565             cell.title = "";
26566             var t = d.getTime();
26567             cell.firstChild.dateValue = t;
26568             if(t == today){
26569                 cell.className += " x-date-today";
26570                 cell.title = cal.todayText;
26571             }
26572             if(t == sel){
26573                 cell.className += " x-date-selected";
26574                 setTimeout(function(){
26575                     try{cell.firstChild.focus();}catch(e){}
26576                 }, 50);
26577             }
26578             // disabling
26579             if(t < min) {
26580                 cell.className = " x-date-disabled";
26581                 cell.title = cal.minText;
26582                 return;
26583             }
26584             if(t > max) {
26585                 cell.className = " x-date-disabled";
26586                 cell.title = cal.maxText;
26587                 return;
26588             }
26589             if(ddays){
26590                 if(ddays.indexOf(d.getDay()) != -1){
26591                     cell.title = ddaysText;
26592                     cell.className = " x-date-disabled";
26593                 }
26594             }
26595             if(ddMatch && format){
26596                 var fvalue = d.dateFormat(format);
26597                 if(ddMatch.test(fvalue)){
26598                     cell.title = ddText.replace("%0", fvalue);
26599                     cell.className = " x-date-disabled";
26600                 }
26601             }
26602         };
26603
26604         var i = 0;
26605         for(; i < startingPos; i++) {
26606             textEls[i].innerHTML = (++prevStart);
26607             d.setDate(d.getDate()+1);
26608             cells[i].className = "x-date-prevday";
26609             setCellClass(this, cells[i]);
26610         }
26611         for(; i < days; i++){
26612             intDay = i - startingPos + 1;
26613             textEls[i].innerHTML = (intDay);
26614             d.setDate(d.getDate()+1);
26615             cells[i].className = "x-date-active";
26616             setCellClass(this, cells[i]);
26617         }
26618         var extraDays = 0;
26619         for(; i < 42; i++) {
26620              textEls[i].innerHTML = (++extraDays);
26621              d.setDate(d.getDate()+1);
26622              cells[i].className = "x-date-nextday";
26623              setCellClass(this, cells[i]);
26624         }
26625
26626         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26627         this.fireEvent('monthchange', this, date);
26628         
26629         if(!this.internalRender){
26630             var main = this.el.dom.firstChild;
26631             var w = main.offsetWidth;
26632             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26633             Roo.fly(main).setWidth(w);
26634             this.internalRender = true;
26635             // opera does not respect the auto grow header center column
26636             // then, after it gets a width opera refuses to recalculate
26637             // without a second pass
26638             if(Roo.isOpera && !this.secondPass){
26639                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26640                 this.secondPass = true;
26641                 this.update.defer(10, this, [date]);
26642             }
26643         }
26644         
26645         
26646     }
26647 });        /*
26648  * Based on:
26649  * Ext JS Library 1.1.1
26650  * Copyright(c) 2006-2007, Ext JS, LLC.
26651  *
26652  * Originally Released Under LGPL - original licence link has changed is not relivant.
26653  *
26654  * Fork - LGPL
26655  * <script type="text/javascript">
26656  */
26657 /**
26658  * @class Roo.TabPanel
26659  * @extends Roo.util.Observable
26660  * A lightweight tab container.
26661  * <br><br>
26662  * Usage:
26663  * <pre><code>
26664 // basic tabs 1, built from existing content
26665 var tabs = new Roo.TabPanel("tabs1");
26666 tabs.addTab("script", "View Script");
26667 tabs.addTab("markup", "View Markup");
26668 tabs.activate("script");
26669
26670 // more advanced tabs, built from javascript
26671 var jtabs = new Roo.TabPanel("jtabs");
26672 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26673
26674 // set up the UpdateManager
26675 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26676 var updater = tab2.getUpdateManager();
26677 updater.setDefaultUrl("ajax1.htm");
26678 tab2.on('activate', updater.refresh, updater, true);
26679
26680 // Use setUrl for Ajax loading
26681 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26682 tab3.setUrl("ajax2.htm", null, true);
26683
26684 // Disabled tab
26685 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26686 tab4.disable();
26687
26688 jtabs.activate("jtabs-1");
26689  * </code></pre>
26690  * @constructor
26691  * Create a new TabPanel.
26692  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26693  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26694  */
26695 Roo.TabPanel = function(container, config){
26696     /**
26697     * The container element for this TabPanel.
26698     * @type Roo.Element
26699     */
26700     this.el = Roo.get(container, true);
26701     if(config){
26702         if(typeof config == "boolean"){
26703             this.tabPosition = config ? "bottom" : "top";
26704         }else{
26705             Roo.apply(this, config);
26706         }
26707     }
26708     if(this.tabPosition == "bottom"){
26709         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26710         this.el.addClass("x-tabs-bottom");
26711     }
26712     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26713     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26714     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26715     if(Roo.isIE){
26716         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26717     }
26718     if(this.tabPosition != "bottom"){
26719         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26720          * @type Roo.Element
26721          */
26722         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26723         this.el.addClass("x-tabs-top");
26724     }
26725     this.items = [];
26726
26727     this.bodyEl.setStyle("position", "relative");
26728
26729     this.active = null;
26730     this.activateDelegate = this.activate.createDelegate(this);
26731
26732     this.addEvents({
26733         /**
26734          * @event tabchange
26735          * Fires when the active tab changes
26736          * @param {Roo.TabPanel} this
26737          * @param {Roo.TabPanelItem} activePanel The new active tab
26738          */
26739         "tabchange": true,
26740         /**
26741          * @event beforetabchange
26742          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26743          * @param {Roo.TabPanel} this
26744          * @param {Object} e Set cancel to true on this object to cancel the tab change
26745          * @param {Roo.TabPanelItem} tab The tab being changed to
26746          */
26747         "beforetabchange" : true
26748     });
26749
26750     Roo.EventManager.onWindowResize(this.onResize, this);
26751     this.cpad = this.el.getPadding("lr");
26752     this.hiddenCount = 0;
26753
26754
26755     // toolbar on the tabbar support...
26756     if (this.toolbar) {
26757         var tcfg = this.toolbar;
26758         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26759         this.toolbar = new Roo.Toolbar(tcfg);
26760         if (Roo.isSafari) {
26761             var tbl = tcfg.container.child('table', true);
26762             tbl.setAttribute('width', '100%');
26763         }
26764         
26765     }
26766    
26767
26768
26769     Roo.TabPanel.superclass.constructor.call(this);
26770 };
26771
26772 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26773     /*
26774      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26775      */
26776     tabPosition : "top",
26777     /*
26778      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26779      */
26780     currentTabWidth : 0,
26781     /*
26782      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26783      */
26784     minTabWidth : 40,
26785     /*
26786      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26787      */
26788     maxTabWidth : 250,
26789     /*
26790      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26791      */
26792     preferredTabWidth : 175,
26793     /*
26794      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26795      */
26796     resizeTabs : false,
26797     /*
26798      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26799      */
26800     monitorResize : true,
26801     /*
26802      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26803      */
26804     toolbar : false,
26805
26806     /**
26807      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26808      * @param {String} id The id of the div to use <b>or create</b>
26809      * @param {String} text The text for the tab
26810      * @param {String} content (optional) Content to put in the TabPanelItem body
26811      * @param {Boolean} closable (optional) True to create a close icon on the tab
26812      * @return {Roo.TabPanelItem} The created TabPanelItem
26813      */
26814     addTab : function(id, text, content, closable){
26815         var item = new Roo.TabPanelItem(this, id, text, closable);
26816         this.addTabItem(item);
26817         if(content){
26818             item.setContent(content);
26819         }
26820         return item;
26821     },
26822
26823     /**
26824      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26825      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26826      * @return {Roo.TabPanelItem}
26827      */
26828     getTab : function(id){
26829         return this.items[id];
26830     },
26831
26832     /**
26833      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26834      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26835      */
26836     hideTab : function(id){
26837         var t = this.items[id];
26838         if(!t.isHidden()){
26839            t.setHidden(true);
26840            this.hiddenCount++;
26841            this.autoSizeTabs();
26842         }
26843     },
26844
26845     /**
26846      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26847      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26848      */
26849     unhideTab : function(id){
26850         var t = this.items[id];
26851         if(t.isHidden()){
26852            t.setHidden(false);
26853            this.hiddenCount--;
26854            this.autoSizeTabs();
26855         }
26856     },
26857
26858     /**
26859      * Adds an existing {@link Roo.TabPanelItem}.
26860      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26861      */
26862     addTabItem : function(item){
26863         this.items[item.id] = item;
26864         this.items.push(item);
26865         if(this.resizeTabs){
26866            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26867            this.autoSizeTabs();
26868         }else{
26869             item.autoSize();
26870         }
26871     },
26872
26873     /**
26874      * Removes a {@link Roo.TabPanelItem}.
26875      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26876      */
26877     removeTab : function(id){
26878         var items = this.items;
26879         var tab = items[id];
26880         if(!tab) { return; }
26881         var index = items.indexOf(tab);
26882         if(this.active == tab && items.length > 1){
26883             var newTab = this.getNextAvailable(index);
26884             if(newTab) {
26885                 newTab.activate();
26886             }
26887         }
26888         this.stripEl.dom.removeChild(tab.pnode.dom);
26889         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26890             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26891         }
26892         items.splice(index, 1);
26893         delete this.items[tab.id];
26894         tab.fireEvent("close", tab);
26895         tab.purgeListeners();
26896         this.autoSizeTabs();
26897     },
26898
26899     getNextAvailable : function(start){
26900         var items = this.items;
26901         var index = start;
26902         // look for a next tab that will slide over to
26903         // replace the one being removed
26904         while(index < items.length){
26905             var item = items[++index];
26906             if(item && !item.isHidden()){
26907                 return item;
26908             }
26909         }
26910         // if one isn't found select the previous tab (on the left)
26911         index = start;
26912         while(index >= 0){
26913             var item = items[--index];
26914             if(item && !item.isHidden()){
26915                 return item;
26916             }
26917         }
26918         return null;
26919     },
26920
26921     /**
26922      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26923      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26924      */
26925     disableTab : function(id){
26926         var tab = this.items[id];
26927         if(tab && this.active != tab){
26928             tab.disable();
26929         }
26930     },
26931
26932     /**
26933      * Enables a {@link Roo.TabPanelItem} that is disabled.
26934      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26935      */
26936     enableTab : function(id){
26937         var tab = this.items[id];
26938         tab.enable();
26939     },
26940
26941     /**
26942      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26943      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26944      * @return {Roo.TabPanelItem} The TabPanelItem.
26945      */
26946     activate : function(id){
26947         var tab = this.items[id];
26948         if(!tab){
26949             return null;
26950         }
26951         if(tab == this.active || tab.disabled){
26952             return tab;
26953         }
26954         var e = {};
26955         this.fireEvent("beforetabchange", this, e, tab);
26956         if(e.cancel !== true && !tab.disabled){
26957             if(this.active){
26958                 this.active.hide();
26959             }
26960             this.active = this.items[id];
26961             this.active.show();
26962             this.fireEvent("tabchange", this, this.active);
26963         }
26964         return tab;
26965     },
26966
26967     /**
26968      * Gets the active {@link Roo.TabPanelItem}.
26969      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26970      */
26971     getActiveTab : function(){
26972         return this.active;
26973     },
26974
26975     /**
26976      * Updates the tab body element to fit the height of the container element
26977      * for overflow scrolling
26978      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26979      */
26980     syncHeight : function(targetHeight){
26981         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26982         var bm = this.bodyEl.getMargins();
26983         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26984         this.bodyEl.setHeight(newHeight);
26985         return newHeight;
26986     },
26987
26988     onResize : function(){
26989         if(this.monitorResize){
26990             this.autoSizeTabs();
26991         }
26992     },
26993
26994     /**
26995      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26996      */
26997     beginUpdate : function(){
26998         this.updating = true;
26999     },
27000
27001     /**
27002      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27003      */
27004     endUpdate : function(){
27005         this.updating = false;
27006         this.autoSizeTabs();
27007     },
27008
27009     /**
27010      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27011      */
27012     autoSizeTabs : function(){
27013         var count = this.items.length;
27014         var vcount = count - this.hiddenCount;
27015         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27016         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27017         var availWidth = Math.floor(w / vcount);
27018         var b = this.stripBody;
27019         if(b.getWidth() > w){
27020             var tabs = this.items;
27021             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27022             if(availWidth < this.minTabWidth){
27023                 /*if(!this.sleft){    // incomplete scrolling code
27024                     this.createScrollButtons();
27025                 }
27026                 this.showScroll();
27027                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27028             }
27029         }else{
27030             if(this.currentTabWidth < this.preferredTabWidth){
27031                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27032             }
27033         }
27034     },
27035
27036     /**
27037      * Returns the number of tabs in this TabPanel.
27038      * @return {Number}
27039      */
27040      getCount : function(){
27041          return this.items.length;
27042      },
27043
27044     /**
27045      * Resizes all the tabs to the passed width
27046      * @param {Number} The new width
27047      */
27048     setTabWidth : function(width){
27049         this.currentTabWidth = width;
27050         for(var i = 0, len = this.items.length; i < len; i++) {
27051                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27052         }
27053     },
27054
27055     /**
27056      * Destroys this TabPanel
27057      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27058      */
27059     destroy : function(removeEl){
27060         Roo.EventManager.removeResizeListener(this.onResize, this);
27061         for(var i = 0, len = this.items.length; i < len; i++){
27062             this.items[i].purgeListeners();
27063         }
27064         if(removeEl === true){
27065             this.el.update("");
27066             this.el.remove();
27067         }
27068     }
27069 });
27070
27071 /**
27072  * @class Roo.TabPanelItem
27073  * @extends Roo.util.Observable
27074  * Represents an individual item (tab plus body) in a TabPanel.
27075  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27076  * @param {String} id The id of this TabPanelItem
27077  * @param {String} text The text for the tab of this TabPanelItem
27078  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27079  */
27080 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27081     /**
27082      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27083      * @type Roo.TabPanel
27084      */
27085     this.tabPanel = tabPanel;
27086     /**
27087      * The id for this TabPanelItem
27088      * @type String
27089      */
27090     this.id = id;
27091     /** @private */
27092     this.disabled = false;
27093     /** @private */
27094     this.text = text;
27095     /** @private */
27096     this.loaded = false;
27097     this.closable = closable;
27098
27099     /**
27100      * The body element for this TabPanelItem.
27101      * @type Roo.Element
27102      */
27103     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27104     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27105     this.bodyEl.setStyle("display", "block");
27106     this.bodyEl.setStyle("zoom", "1");
27107     this.hideAction();
27108
27109     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27110     /** @private */
27111     this.el = Roo.get(els.el, true);
27112     this.inner = Roo.get(els.inner, true);
27113     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27114     this.pnode = Roo.get(els.el.parentNode, true);
27115     this.el.on("mousedown", this.onTabMouseDown, this);
27116     this.el.on("click", this.onTabClick, this);
27117     /** @private */
27118     if(closable){
27119         var c = Roo.get(els.close, true);
27120         c.dom.title = this.closeText;
27121         c.addClassOnOver("close-over");
27122         c.on("click", this.closeClick, this);
27123      }
27124
27125     this.addEvents({
27126          /**
27127          * @event activate
27128          * Fires when this tab becomes the active tab.
27129          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27130          * @param {Roo.TabPanelItem} this
27131          */
27132         "activate": true,
27133         /**
27134          * @event beforeclose
27135          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27136          * @param {Roo.TabPanelItem} this
27137          * @param {Object} e Set cancel to true on this object to cancel the close.
27138          */
27139         "beforeclose": true,
27140         /**
27141          * @event close
27142          * Fires when this tab is closed.
27143          * @param {Roo.TabPanelItem} this
27144          */
27145          "close": true,
27146         /**
27147          * @event deactivate
27148          * Fires when this tab is no longer the active tab.
27149          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27150          * @param {Roo.TabPanelItem} this
27151          */
27152          "deactivate" : true
27153     });
27154     this.hidden = false;
27155
27156     Roo.TabPanelItem.superclass.constructor.call(this);
27157 };
27158
27159 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27160     purgeListeners : function(){
27161        Roo.util.Observable.prototype.purgeListeners.call(this);
27162        this.el.removeAllListeners();
27163     },
27164     /**
27165      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27166      */
27167     show : function(){
27168         this.pnode.addClass("on");
27169         this.showAction();
27170         if(Roo.isOpera){
27171             this.tabPanel.stripWrap.repaint();
27172         }
27173         this.fireEvent("activate", this.tabPanel, this);
27174     },
27175
27176     /**
27177      * Returns true if this tab is the active tab.
27178      * @return {Boolean}
27179      */
27180     isActive : function(){
27181         return this.tabPanel.getActiveTab() == this;
27182     },
27183
27184     /**
27185      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27186      */
27187     hide : function(){
27188         this.pnode.removeClass("on");
27189         this.hideAction();
27190         this.fireEvent("deactivate", this.tabPanel, this);
27191     },
27192
27193     hideAction : function(){
27194         this.bodyEl.hide();
27195         this.bodyEl.setStyle("position", "absolute");
27196         this.bodyEl.setLeft("-20000px");
27197         this.bodyEl.setTop("-20000px");
27198     },
27199
27200     showAction : function(){
27201         this.bodyEl.setStyle("position", "relative");
27202         this.bodyEl.setTop("");
27203         this.bodyEl.setLeft("");
27204         this.bodyEl.show();
27205     },
27206
27207     /**
27208      * Set the tooltip for the tab.
27209      * @param {String} tooltip The tab's tooltip
27210      */
27211     setTooltip : function(text){
27212         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27213             this.textEl.dom.qtip = text;
27214             this.textEl.dom.removeAttribute('title');
27215         }else{
27216             this.textEl.dom.title = text;
27217         }
27218     },
27219
27220     onTabClick : function(e){
27221         e.preventDefault();
27222         this.tabPanel.activate(this.id);
27223     },
27224
27225     onTabMouseDown : function(e){
27226         e.preventDefault();
27227         this.tabPanel.activate(this.id);
27228     },
27229
27230     getWidth : function(){
27231         return this.inner.getWidth();
27232     },
27233
27234     setWidth : function(width){
27235         var iwidth = width - this.pnode.getPadding("lr");
27236         this.inner.setWidth(iwidth);
27237         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27238         this.pnode.setWidth(width);
27239     },
27240
27241     /**
27242      * Show or hide the tab
27243      * @param {Boolean} hidden True to hide or false to show.
27244      */
27245     setHidden : function(hidden){
27246         this.hidden = hidden;
27247         this.pnode.setStyle("display", hidden ? "none" : "");
27248     },
27249
27250     /**
27251      * Returns true if this tab is "hidden"
27252      * @return {Boolean}
27253      */
27254     isHidden : function(){
27255         return this.hidden;
27256     },
27257
27258     /**
27259      * Returns the text for this tab
27260      * @return {String}
27261      */
27262     getText : function(){
27263         return this.text;
27264     },
27265
27266     autoSize : function(){
27267         //this.el.beginMeasure();
27268         this.textEl.setWidth(1);
27269         /*
27270          *  #2804 [new] Tabs in Roojs
27271          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27272          */
27273         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27274         //this.el.endMeasure();
27275     },
27276
27277     /**
27278      * Sets the text for the tab (Note: this also sets the tooltip text)
27279      * @param {String} text The tab's text and tooltip
27280      */
27281     setText : function(text){
27282         this.text = text;
27283         this.textEl.update(text);
27284         this.setTooltip(text);
27285         if(!this.tabPanel.resizeTabs){
27286             this.autoSize();
27287         }
27288     },
27289     /**
27290      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27291      */
27292     activate : function(){
27293         this.tabPanel.activate(this.id);
27294     },
27295
27296     /**
27297      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27298      */
27299     disable : function(){
27300         if(this.tabPanel.active != this){
27301             this.disabled = true;
27302             this.pnode.addClass("disabled");
27303         }
27304     },
27305
27306     /**
27307      * Enables this TabPanelItem if it was previously disabled.
27308      */
27309     enable : function(){
27310         this.disabled = false;
27311         this.pnode.removeClass("disabled");
27312     },
27313
27314     /**
27315      * Sets the content for this TabPanelItem.
27316      * @param {String} content The content
27317      * @param {Boolean} loadScripts true to look for and load scripts
27318      */
27319     setContent : function(content, loadScripts){
27320         this.bodyEl.update(content, loadScripts);
27321     },
27322
27323     /**
27324      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27325      * @return {Roo.UpdateManager} The UpdateManager
27326      */
27327     getUpdateManager : function(){
27328         return this.bodyEl.getUpdateManager();
27329     },
27330
27331     /**
27332      * Set a URL to be used to load the content for this TabPanelItem.
27333      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27334      * @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)
27335      * @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)
27336      * @return {Roo.UpdateManager} The UpdateManager
27337      */
27338     setUrl : function(url, params, loadOnce){
27339         if(this.refreshDelegate){
27340             this.un('activate', this.refreshDelegate);
27341         }
27342         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27343         this.on("activate", this.refreshDelegate);
27344         return this.bodyEl.getUpdateManager();
27345     },
27346
27347     /** @private */
27348     _handleRefresh : function(url, params, loadOnce){
27349         if(!loadOnce || !this.loaded){
27350             var updater = this.bodyEl.getUpdateManager();
27351             updater.update(url, params, this._setLoaded.createDelegate(this));
27352         }
27353     },
27354
27355     /**
27356      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27357      *   Will fail silently if the setUrl method has not been called.
27358      *   This does not activate the panel, just updates its content.
27359      */
27360     refresh : function(){
27361         if(this.refreshDelegate){
27362            this.loaded = false;
27363            this.refreshDelegate();
27364         }
27365     },
27366
27367     /** @private */
27368     _setLoaded : function(){
27369         this.loaded = true;
27370     },
27371
27372     /** @private */
27373     closeClick : function(e){
27374         var o = {};
27375         e.stopEvent();
27376         this.fireEvent("beforeclose", this, o);
27377         if(o.cancel !== true){
27378             this.tabPanel.removeTab(this.id);
27379         }
27380     },
27381     /**
27382      * The text displayed in the tooltip for the close icon.
27383      * @type String
27384      */
27385     closeText : "Close this tab"
27386 });
27387
27388 /** @private */
27389 Roo.TabPanel.prototype.createStrip = function(container){
27390     var strip = document.createElement("div");
27391     strip.className = "x-tabs-wrap";
27392     container.appendChild(strip);
27393     return strip;
27394 };
27395 /** @private */
27396 Roo.TabPanel.prototype.createStripList = function(strip){
27397     // div wrapper for retard IE
27398     // returns the "tr" element.
27399     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27400         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27401         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27402     return strip.firstChild.firstChild.firstChild.firstChild;
27403 };
27404 /** @private */
27405 Roo.TabPanel.prototype.createBody = function(container){
27406     var body = document.createElement("div");
27407     Roo.id(body, "tab-body");
27408     Roo.fly(body).addClass("x-tabs-body");
27409     container.appendChild(body);
27410     return body;
27411 };
27412 /** @private */
27413 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27414     var body = Roo.getDom(id);
27415     if(!body){
27416         body = document.createElement("div");
27417         body.id = id;
27418     }
27419     Roo.fly(body).addClass("x-tabs-item-body");
27420     bodyEl.insertBefore(body, bodyEl.firstChild);
27421     return body;
27422 };
27423 /** @private */
27424 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27425     var td = document.createElement("td");
27426     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27427     //stripEl.appendChild(td);
27428     if(closable){
27429         td.className = "x-tabs-closable";
27430         if(!this.closeTpl){
27431             this.closeTpl = new Roo.Template(
27432                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27433                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27434                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27435             );
27436         }
27437         var el = this.closeTpl.overwrite(td, {"text": text});
27438         var close = el.getElementsByTagName("div")[0];
27439         var inner = el.getElementsByTagName("em")[0];
27440         return {"el": el, "close": close, "inner": inner};
27441     } else {
27442         if(!this.tabTpl){
27443             this.tabTpl = new Roo.Template(
27444                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27445                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27446             );
27447         }
27448         var el = this.tabTpl.overwrite(td, {"text": text});
27449         var inner = el.getElementsByTagName("em")[0];
27450         return {"el": el, "inner": inner};
27451     }
27452 };/*
27453  * Based on:
27454  * Ext JS Library 1.1.1
27455  * Copyright(c) 2006-2007, Ext JS, LLC.
27456  *
27457  * Originally Released Under LGPL - original licence link has changed is not relivant.
27458  *
27459  * Fork - LGPL
27460  * <script type="text/javascript">
27461  */
27462
27463 /**
27464  * @class Roo.Button
27465  * @extends Roo.util.Observable
27466  * Simple Button class
27467  * @cfg {String} text The button text
27468  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27469  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27470  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27471  * @cfg {Object} scope The scope of the handler
27472  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27473  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27474  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27475  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27476  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27477  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27478    applies if enableToggle = true)
27479  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27480  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27481   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27482  * @constructor
27483  * Create a new button
27484  * @param {Object} config The config object
27485  */
27486 Roo.Button = function(renderTo, config)
27487 {
27488     if (!config) {
27489         config = renderTo;
27490         renderTo = config.renderTo || false;
27491     }
27492     
27493     Roo.apply(this, config);
27494     this.addEvents({
27495         /**
27496              * @event click
27497              * Fires when this button is clicked
27498              * @param {Button} this
27499              * @param {EventObject} e The click event
27500              */
27501             "click" : true,
27502         /**
27503              * @event toggle
27504              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27505              * @param {Button} this
27506              * @param {Boolean} pressed
27507              */
27508             "toggle" : true,
27509         /**
27510              * @event mouseover
27511              * Fires when the mouse hovers over the button
27512              * @param {Button} this
27513              * @param {Event} e The event object
27514              */
27515         'mouseover' : true,
27516         /**
27517              * @event mouseout
27518              * Fires when the mouse exits the button
27519              * @param {Button} this
27520              * @param {Event} e The event object
27521              */
27522         'mouseout': true,
27523          /**
27524              * @event render
27525              * Fires when the button is rendered
27526              * @param {Button} this
27527              */
27528         'render': true
27529     });
27530     if(this.menu){
27531         this.menu = Roo.menu.MenuMgr.get(this.menu);
27532     }
27533     // register listeners first!!  - so render can be captured..
27534     Roo.util.Observable.call(this);
27535     if(renderTo){
27536         this.render(renderTo);
27537     }
27538     
27539   
27540 };
27541
27542 Roo.extend(Roo.Button, Roo.util.Observable, {
27543     /**
27544      * 
27545      */
27546     
27547     /**
27548      * Read-only. True if this button is hidden
27549      * @type Boolean
27550      */
27551     hidden : false,
27552     /**
27553      * Read-only. True if this button is disabled
27554      * @type Boolean
27555      */
27556     disabled : false,
27557     /**
27558      * Read-only. True if this button is pressed (only if enableToggle = true)
27559      * @type Boolean
27560      */
27561     pressed : false,
27562
27563     /**
27564      * @cfg {Number} tabIndex 
27565      * The DOM tabIndex for this button (defaults to undefined)
27566      */
27567     tabIndex : undefined,
27568
27569     /**
27570      * @cfg {Boolean} enableToggle
27571      * True to enable pressed/not pressed toggling (defaults to false)
27572      */
27573     enableToggle: false,
27574     /**
27575      * @cfg {Mixed} menu
27576      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27577      */
27578     menu : undefined,
27579     /**
27580      * @cfg {String} menuAlign
27581      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27582      */
27583     menuAlign : "tl-bl?",
27584
27585     /**
27586      * @cfg {String} iconCls
27587      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27588      */
27589     iconCls : undefined,
27590     /**
27591      * @cfg {String} type
27592      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27593      */
27594     type : 'button',
27595
27596     // private
27597     menuClassTarget: 'tr',
27598
27599     /**
27600      * @cfg {String} clickEvent
27601      * The type of event to map to the button's event handler (defaults to 'click')
27602      */
27603     clickEvent : 'click',
27604
27605     /**
27606      * @cfg {Boolean} handleMouseEvents
27607      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27608      */
27609     handleMouseEvents : true,
27610
27611     /**
27612      * @cfg {String} tooltipType
27613      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27614      */
27615     tooltipType : 'qtip',
27616
27617     /**
27618      * @cfg {String} cls
27619      * A CSS class to apply to the button's main element.
27620      */
27621     
27622     /**
27623      * @cfg {Roo.Template} template (Optional)
27624      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27625      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27626      * require code modifications if required elements (e.g. a button) aren't present.
27627      */
27628
27629     // private
27630     render : function(renderTo){
27631         var btn;
27632         if(this.hideParent){
27633             this.parentEl = Roo.get(renderTo);
27634         }
27635         if(!this.dhconfig){
27636             if(!this.template){
27637                 if(!Roo.Button.buttonTemplate){
27638                     // hideous table template
27639                     Roo.Button.buttonTemplate = new Roo.Template(
27640                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27641                         '<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>',
27642                         "</tr></tbody></table>");
27643                 }
27644                 this.template = Roo.Button.buttonTemplate;
27645             }
27646             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27647             var btnEl = btn.child("button:first");
27648             btnEl.on('focus', this.onFocus, this);
27649             btnEl.on('blur', this.onBlur, this);
27650             if(this.cls){
27651                 btn.addClass(this.cls);
27652             }
27653             if(this.icon){
27654                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27655             }
27656             if(this.iconCls){
27657                 btnEl.addClass(this.iconCls);
27658                 if(!this.cls){
27659                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27660                 }
27661             }
27662             if(this.tabIndex !== undefined){
27663                 btnEl.dom.tabIndex = this.tabIndex;
27664             }
27665             if(this.tooltip){
27666                 if(typeof this.tooltip == 'object'){
27667                     Roo.QuickTips.tips(Roo.apply({
27668                           target: btnEl.id
27669                     }, this.tooltip));
27670                 } else {
27671                     btnEl.dom[this.tooltipType] = this.tooltip;
27672                 }
27673             }
27674         }else{
27675             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27676         }
27677         this.el = btn;
27678         if(this.id){
27679             this.el.dom.id = this.el.id = this.id;
27680         }
27681         if(this.menu){
27682             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27683             this.menu.on("show", this.onMenuShow, this);
27684             this.menu.on("hide", this.onMenuHide, this);
27685         }
27686         btn.addClass("x-btn");
27687         if(Roo.isIE && !Roo.isIE7){
27688             this.autoWidth.defer(1, this);
27689         }else{
27690             this.autoWidth();
27691         }
27692         if(this.handleMouseEvents){
27693             btn.on("mouseover", this.onMouseOver, this);
27694             btn.on("mouseout", this.onMouseOut, this);
27695             btn.on("mousedown", this.onMouseDown, this);
27696         }
27697         btn.on(this.clickEvent, this.onClick, this);
27698         //btn.on("mouseup", this.onMouseUp, this);
27699         if(this.hidden){
27700             this.hide();
27701         }
27702         if(this.disabled){
27703             this.disable();
27704         }
27705         Roo.ButtonToggleMgr.register(this);
27706         if(this.pressed){
27707             this.el.addClass("x-btn-pressed");
27708         }
27709         if(this.repeat){
27710             var repeater = new Roo.util.ClickRepeater(btn,
27711                 typeof this.repeat == "object" ? this.repeat : {}
27712             );
27713             repeater.on("click", this.onClick,  this);
27714         }
27715         
27716         this.fireEvent('render', this);
27717         
27718     },
27719     /**
27720      * Returns the button's underlying element
27721      * @return {Roo.Element} The element
27722      */
27723     getEl : function(){
27724         return this.el;  
27725     },
27726     
27727     /**
27728      * Destroys this Button and removes any listeners.
27729      */
27730     destroy : function(){
27731         Roo.ButtonToggleMgr.unregister(this);
27732         this.el.removeAllListeners();
27733         this.purgeListeners();
27734         this.el.remove();
27735     },
27736
27737     // private
27738     autoWidth : function(){
27739         if(this.el){
27740             this.el.setWidth("auto");
27741             if(Roo.isIE7 && Roo.isStrict){
27742                 var ib = this.el.child('button');
27743                 if(ib && ib.getWidth() > 20){
27744                     ib.clip();
27745                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27746                 }
27747             }
27748             if(this.minWidth){
27749                 if(this.hidden){
27750                     this.el.beginMeasure();
27751                 }
27752                 if(this.el.getWidth() < this.minWidth){
27753                     this.el.setWidth(this.minWidth);
27754                 }
27755                 if(this.hidden){
27756                     this.el.endMeasure();
27757                 }
27758             }
27759         }
27760     },
27761
27762     /**
27763      * Assigns this button's click handler
27764      * @param {Function} handler The function to call when the button is clicked
27765      * @param {Object} scope (optional) Scope for the function passed in
27766      */
27767     setHandler : function(handler, scope){
27768         this.handler = handler;
27769         this.scope = scope;  
27770     },
27771     
27772     /**
27773      * Sets this button's text
27774      * @param {String} text The button text
27775      */
27776     setText : function(text){
27777         this.text = text;
27778         if(this.el){
27779             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27780         }
27781         this.autoWidth();
27782     },
27783     
27784     /**
27785      * Gets the text for this button
27786      * @return {String} The button text
27787      */
27788     getText : function(){
27789         return this.text;  
27790     },
27791     
27792     /**
27793      * Show this button
27794      */
27795     show: function(){
27796         this.hidden = false;
27797         if(this.el){
27798             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27799         }
27800     },
27801     
27802     /**
27803      * Hide this button
27804      */
27805     hide: function(){
27806         this.hidden = true;
27807         if(this.el){
27808             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27809         }
27810     },
27811     
27812     /**
27813      * Convenience function for boolean show/hide
27814      * @param {Boolean} visible True to show, false to hide
27815      */
27816     setVisible: function(visible){
27817         if(visible) {
27818             this.show();
27819         }else{
27820             this.hide();
27821         }
27822     },
27823     
27824     /**
27825      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27826      * @param {Boolean} state (optional) Force a particular state
27827      */
27828     toggle : function(state){
27829         state = state === undefined ? !this.pressed : state;
27830         if(state != this.pressed){
27831             if(state){
27832                 this.el.addClass("x-btn-pressed");
27833                 this.pressed = true;
27834                 this.fireEvent("toggle", this, true);
27835             }else{
27836                 this.el.removeClass("x-btn-pressed");
27837                 this.pressed = false;
27838                 this.fireEvent("toggle", this, false);
27839             }
27840             if(this.toggleHandler){
27841                 this.toggleHandler.call(this.scope || this, this, state);
27842             }
27843         }
27844     },
27845     
27846     /**
27847      * Focus the button
27848      */
27849     focus : function(){
27850         this.el.child('button:first').focus();
27851     },
27852     
27853     /**
27854      * Disable this button
27855      */
27856     disable : function(){
27857         if(this.el){
27858             this.el.addClass("x-btn-disabled");
27859         }
27860         this.disabled = true;
27861     },
27862     
27863     /**
27864      * Enable this button
27865      */
27866     enable : function(){
27867         if(this.el){
27868             this.el.removeClass("x-btn-disabled");
27869         }
27870         this.disabled = false;
27871     },
27872
27873     /**
27874      * Convenience function for boolean enable/disable
27875      * @param {Boolean} enabled True to enable, false to disable
27876      */
27877     setDisabled : function(v){
27878         this[v !== true ? "enable" : "disable"]();
27879     },
27880
27881     // private
27882     onClick : function(e)
27883     {
27884         if(e){
27885             e.preventDefault();
27886         }
27887         if(e.button != 0){
27888             return;
27889         }
27890         if(!this.disabled){
27891             if(this.enableToggle){
27892                 this.toggle();
27893             }
27894             if(this.menu && !this.menu.isVisible()){
27895                 this.menu.show(this.el, this.menuAlign);
27896             }
27897             this.fireEvent("click", this, e);
27898             if(this.handler){
27899                 this.el.removeClass("x-btn-over");
27900                 this.handler.call(this.scope || this, this, e);
27901             }
27902         }
27903     },
27904     // private
27905     onMouseOver : function(e){
27906         if(!this.disabled){
27907             this.el.addClass("x-btn-over");
27908             this.fireEvent('mouseover', this, e);
27909         }
27910     },
27911     // private
27912     onMouseOut : function(e){
27913         if(!e.within(this.el,  true)){
27914             this.el.removeClass("x-btn-over");
27915             this.fireEvent('mouseout', this, e);
27916         }
27917     },
27918     // private
27919     onFocus : function(e){
27920         if(!this.disabled){
27921             this.el.addClass("x-btn-focus");
27922         }
27923     },
27924     // private
27925     onBlur : function(e){
27926         this.el.removeClass("x-btn-focus");
27927     },
27928     // private
27929     onMouseDown : function(e){
27930         if(!this.disabled && e.button == 0){
27931             this.el.addClass("x-btn-click");
27932             Roo.get(document).on('mouseup', this.onMouseUp, this);
27933         }
27934     },
27935     // private
27936     onMouseUp : function(e){
27937         if(e.button == 0){
27938             this.el.removeClass("x-btn-click");
27939             Roo.get(document).un('mouseup', this.onMouseUp, this);
27940         }
27941     },
27942     // private
27943     onMenuShow : function(e){
27944         this.el.addClass("x-btn-menu-active");
27945     },
27946     // private
27947     onMenuHide : function(e){
27948         this.el.removeClass("x-btn-menu-active");
27949     }   
27950 });
27951
27952 // Private utility class used by Button
27953 Roo.ButtonToggleMgr = function(){
27954    var groups = {};
27955    
27956    function toggleGroup(btn, state){
27957        if(state){
27958            var g = groups[btn.toggleGroup];
27959            for(var i = 0, l = g.length; i < l; i++){
27960                if(g[i] != btn){
27961                    g[i].toggle(false);
27962                }
27963            }
27964        }
27965    }
27966    
27967    return {
27968        register : function(btn){
27969            if(!btn.toggleGroup){
27970                return;
27971            }
27972            var g = groups[btn.toggleGroup];
27973            if(!g){
27974                g = groups[btn.toggleGroup] = [];
27975            }
27976            g.push(btn);
27977            btn.on("toggle", toggleGroup);
27978        },
27979        
27980        unregister : function(btn){
27981            if(!btn.toggleGroup){
27982                return;
27983            }
27984            var g = groups[btn.toggleGroup];
27985            if(g){
27986                g.remove(btn);
27987                btn.un("toggle", toggleGroup);
27988            }
27989        }
27990    };
27991 }();/*
27992  * Based on:
27993  * Ext JS Library 1.1.1
27994  * Copyright(c) 2006-2007, Ext JS, LLC.
27995  *
27996  * Originally Released Under LGPL - original licence link has changed is not relivant.
27997  *
27998  * Fork - LGPL
27999  * <script type="text/javascript">
28000  */
28001  
28002 /**
28003  * @class Roo.SplitButton
28004  * @extends Roo.Button
28005  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28006  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28007  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28008  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28009  * @cfg {String} arrowTooltip The title attribute of the arrow
28010  * @constructor
28011  * Create a new menu button
28012  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28013  * @param {Object} config The config object
28014  */
28015 Roo.SplitButton = function(renderTo, config){
28016     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28017     /**
28018      * @event arrowclick
28019      * Fires when this button's arrow is clicked
28020      * @param {SplitButton} this
28021      * @param {EventObject} e The click event
28022      */
28023     this.addEvents({"arrowclick":true});
28024 };
28025
28026 Roo.extend(Roo.SplitButton, Roo.Button, {
28027     render : function(renderTo){
28028         // this is one sweet looking template!
28029         var tpl = new Roo.Template(
28030             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28031             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28032             '<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>',
28033             "</tbody></table></td><td>",
28034             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28035             '<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>',
28036             "</tbody></table></td></tr></table>"
28037         );
28038         var btn = tpl.append(renderTo, [this.text, this.type], true);
28039         var btnEl = btn.child("button");
28040         if(this.cls){
28041             btn.addClass(this.cls);
28042         }
28043         if(this.icon){
28044             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28045         }
28046         if(this.iconCls){
28047             btnEl.addClass(this.iconCls);
28048             if(!this.cls){
28049                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28050             }
28051         }
28052         this.el = btn;
28053         if(this.handleMouseEvents){
28054             btn.on("mouseover", this.onMouseOver, this);
28055             btn.on("mouseout", this.onMouseOut, this);
28056             btn.on("mousedown", this.onMouseDown, this);
28057             btn.on("mouseup", this.onMouseUp, this);
28058         }
28059         btn.on(this.clickEvent, this.onClick, this);
28060         if(this.tooltip){
28061             if(typeof this.tooltip == 'object'){
28062                 Roo.QuickTips.tips(Roo.apply({
28063                       target: btnEl.id
28064                 }, this.tooltip));
28065             } else {
28066                 btnEl.dom[this.tooltipType] = this.tooltip;
28067             }
28068         }
28069         if(this.arrowTooltip){
28070             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28071         }
28072         if(this.hidden){
28073             this.hide();
28074         }
28075         if(this.disabled){
28076             this.disable();
28077         }
28078         if(this.pressed){
28079             this.el.addClass("x-btn-pressed");
28080         }
28081         if(Roo.isIE && !Roo.isIE7){
28082             this.autoWidth.defer(1, this);
28083         }else{
28084             this.autoWidth();
28085         }
28086         if(this.menu){
28087             this.menu.on("show", this.onMenuShow, this);
28088             this.menu.on("hide", this.onMenuHide, this);
28089         }
28090         this.fireEvent('render', this);
28091     },
28092
28093     // private
28094     autoWidth : function(){
28095         if(this.el){
28096             var tbl = this.el.child("table:first");
28097             var tbl2 = this.el.child("table:last");
28098             this.el.setWidth("auto");
28099             tbl.setWidth("auto");
28100             if(Roo.isIE7 && Roo.isStrict){
28101                 var ib = this.el.child('button:first');
28102                 if(ib && ib.getWidth() > 20){
28103                     ib.clip();
28104                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28105                 }
28106             }
28107             if(this.minWidth){
28108                 if(this.hidden){
28109                     this.el.beginMeasure();
28110                 }
28111                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28112                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28113                 }
28114                 if(this.hidden){
28115                     this.el.endMeasure();
28116                 }
28117             }
28118             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28119         } 
28120     },
28121     /**
28122      * Sets this button's click handler
28123      * @param {Function} handler The function to call when the button is clicked
28124      * @param {Object} scope (optional) Scope for the function passed above
28125      */
28126     setHandler : function(handler, scope){
28127         this.handler = handler;
28128         this.scope = scope;  
28129     },
28130     
28131     /**
28132      * Sets this button's arrow click handler
28133      * @param {Function} handler The function to call when the arrow is clicked
28134      * @param {Object} scope (optional) Scope for the function passed above
28135      */
28136     setArrowHandler : function(handler, scope){
28137         this.arrowHandler = handler;
28138         this.scope = scope;  
28139     },
28140     
28141     /**
28142      * Focus the button
28143      */
28144     focus : function(){
28145         if(this.el){
28146             this.el.child("button:first").focus();
28147         }
28148     },
28149
28150     // private
28151     onClick : function(e){
28152         e.preventDefault();
28153         if(!this.disabled){
28154             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28155                 if(this.menu && !this.menu.isVisible()){
28156                     this.menu.show(this.el, this.menuAlign);
28157                 }
28158                 this.fireEvent("arrowclick", this, e);
28159                 if(this.arrowHandler){
28160                     this.arrowHandler.call(this.scope || this, this, e);
28161                 }
28162             }else{
28163                 this.fireEvent("click", this, e);
28164                 if(this.handler){
28165                     this.handler.call(this.scope || this, this, e);
28166                 }
28167             }
28168         }
28169     },
28170     // private
28171     onMouseDown : function(e){
28172         if(!this.disabled){
28173             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28174         }
28175     },
28176     // private
28177     onMouseUp : function(e){
28178         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28179     }   
28180 });
28181
28182
28183 // backwards compat
28184 Roo.MenuButton = Roo.SplitButton;/*
28185  * Based on:
28186  * Ext JS Library 1.1.1
28187  * Copyright(c) 2006-2007, Ext JS, LLC.
28188  *
28189  * Originally Released Under LGPL - original licence link has changed is not relivant.
28190  *
28191  * Fork - LGPL
28192  * <script type="text/javascript">
28193  */
28194
28195 /**
28196  * @class Roo.Toolbar
28197  * Basic Toolbar class.
28198  * @constructor
28199  * Creates a new Toolbar
28200  * @param {Object} container The config object
28201  */ 
28202 Roo.Toolbar = function(container, buttons, config)
28203 {
28204     /// old consturctor format still supported..
28205     if(container instanceof Array){ // omit the container for later rendering
28206         buttons = container;
28207         config = buttons;
28208         container = null;
28209     }
28210     if (typeof(container) == 'object' && container.xtype) {
28211         config = container;
28212         container = config.container;
28213         buttons = config.buttons || []; // not really - use items!!
28214     }
28215     var xitems = [];
28216     if (config && config.items) {
28217         xitems = config.items;
28218         delete config.items;
28219     }
28220     Roo.apply(this, config);
28221     this.buttons = buttons;
28222     
28223     if(container){
28224         this.render(container);
28225     }
28226     this.xitems = xitems;
28227     Roo.each(xitems, function(b) {
28228         this.add(b);
28229     }, this);
28230     
28231 };
28232
28233 Roo.Toolbar.prototype = {
28234     /**
28235      * @cfg {Array} items
28236      * array of button configs or elements to add (will be converted to a MixedCollection)
28237      */
28238     
28239     /**
28240      * @cfg {String/HTMLElement/Element} container
28241      * The id or element that will contain the toolbar
28242      */
28243     // private
28244     render : function(ct){
28245         this.el = Roo.get(ct);
28246         if(this.cls){
28247             this.el.addClass(this.cls);
28248         }
28249         // using a table allows for vertical alignment
28250         // 100% width is needed by Safari...
28251         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28252         this.tr = this.el.child("tr", true);
28253         var autoId = 0;
28254         this.items = new Roo.util.MixedCollection(false, function(o){
28255             return o.id || ("item" + (++autoId));
28256         });
28257         if(this.buttons){
28258             this.add.apply(this, this.buttons);
28259             delete this.buttons;
28260         }
28261     },
28262
28263     /**
28264      * Adds element(s) to the toolbar -- this function takes a variable number of 
28265      * arguments of mixed type and adds them to the toolbar.
28266      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28267      * <ul>
28268      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28269      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28270      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28271      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28272      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28273      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28274      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28275      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28276      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28277      * </ul>
28278      * @param {Mixed} arg2
28279      * @param {Mixed} etc.
28280      */
28281     add : function(){
28282         var a = arguments, l = a.length;
28283         for(var i = 0; i < l; i++){
28284             this._add(a[i]);
28285         }
28286     },
28287     // private..
28288     _add : function(el) {
28289         
28290         if (el.xtype) {
28291             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28292         }
28293         
28294         if (el.applyTo){ // some kind of form field
28295             return this.addField(el);
28296         } 
28297         if (el.render){ // some kind of Toolbar.Item
28298             return this.addItem(el);
28299         }
28300         if (typeof el == "string"){ // string
28301             if(el == "separator" || el == "-"){
28302                 return this.addSeparator();
28303             }
28304             if (el == " "){
28305                 return this.addSpacer();
28306             }
28307             if(el == "->"){
28308                 return this.addFill();
28309             }
28310             return this.addText(el);
28311             
28312         }
28313         if(el.tagName){ // element
28314             return this.addElement(el);
28315         }
28316         if(typeof el == "object"){ // must be button config?
28317             return this.addButton(el);
28318         }
28319         // and now what?!?!
28320         return false;
28321         
28322     },
28323     
28324     /**
28325      * Add an Xtype element
28326      * @param {Object} xtype Xtype Object
28327      * @return {Object} created Object
28328      */
28329     addxtype : function(e){
28330         return this.add(e);  
28331     },
28332     
28333     /**
28334      * Returns the Element for this toolbar.
28335      * @return {Roo.Element}
28336      */
28337     getEl : function(){
28338         return this.el;  
28339     },
28340     
28341     /**
28342      * Adds a separator
28343      * @return {Roo.Toolbar.Item} The separator item
28344      */
28345     addSeparator : function(){
28346         return this.addItem(new Roo.Toolbar.Separator());
28347     },
28348
28349     /**
28350      * Adds a spacer element
28351      * @return {Roo.Toolbar.Spacer} The spacer item
28352      */
28353     addSpacer : function(){
28354         return this.addItem(new Roo.Toolbar.Spacer());
28355     },
28356
28357     /**
28358      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28359      * @return {Roo.Toolbar.Fill} The fill item
28360      */
28361     addFill : function(){
28362         return this.addItem(new Roo.Toolbar.Fill());
28363     },
28364
28365     /**
28366      * Adds any standard HTML element to the toolbar
28367      * @param {String/HTMLElement/Element} el The element or id of the element to add
28368      * @return {Roo.Toolbar.Item} The element's item
28369      */
28370     addElement : function(el){
28371         return this.addItem(new Roo.Toolbar.Item(el));
28372     },
28373     /**
28374      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28375      * @type Roo.util.MixedCollection  
28376      */
28377     items : false,
28378      
28379     /**
28380      * Adds any Toolbar.Item or subclass
28381      * @param {Roo.Toolbar.Item} item
28382      * @return {Roo.Toolbar.Item} The item
28383      */
28384     addItem : function(item){
28385         var td = this.nextBlock();
28386         item.render(td);
28387         this.items.add(item);
28388         return item;
28389     },
28390     
28391     /**
28392      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28393      * @param {Object/Array} config A button config or array of configs
28394      * @return {Roo.Toolbar.Button/Array}
28395      */
28396     addButton : function(config){
28397         if(config instanceof Array){
28398             var buttons = [];
28399             for(var i = 0, len = config.length; i < len; i++) {
28400                 buttons.push(this.addButton(config[i]));
28401             }
28402             return buttons;
28403         }
28404         var b = config;
28405         if(!(config instanceof Roo.Toolbar.Button)){
28406             b = config.split ?
28407                 new Roo.Toolbar.SplitButton(config) :
28408                 new Roo.Toolbar.Button(config);
28409         }
28410         var td = this.nextBlock();
28411         b.render(td);
28412         this.items.add(b);
28413         return b;
28414     },
28415     
28416     /**
28417      * Adds text to the toolbar
28418      * @param {String} text The text to add
28419      * @return {Roo.Toolbar.Item} The element's item
28420      */
28421     addText : function(text){
28422         return this.addItem(new Roo.Toolbar.TextItem(text));
28423     },
28424     
28425     /**
28426      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28427      * @param {Number} index The index where the item is to be inserted
28428      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28429      * @return {Roo.Toolbar.Button/Item}
28430      */
28431     insertButton : function(index, item){
28432         if(item instanceof Array){
28433             var buttons = [];
28434             for(var i = 0, len = item.length; i < len; i++) {
28435                buttons.push(this.insertButton(index + i, item[i]));
28436             }
28437             return buttons;
28438         }
28439         if (!(item instanceof Roo.Toolbar.Button)){
28440            item = new Roo.Toolbar.Button(item);
28441         }
28442         var td = document.createElement("td");
28443         this.tr.insertBefore(td, this.tr.childNodes[index]);
28444         item.render(td);
28445         this.items.insert(index, item);
28446         return item;
28447     },
28448     
28449     /**
28450      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28451      * @param {Object} config
28452      * @return {Roo.Toolbar.Item} The element's item
28453      */
28454     addDom : function(config, returnEl){
28455         var td = this.nextBlock();
28456         Roo.DomHelper.overwrite(td, config);
28457         var ti = new Roo.Toolbar.Item(td.firstChild);
28458         ti.render(td);
28459         this.items.add(ti);
28460         return ti;
28461     },
28462
28463     /**
28464      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28465      * @type Roo.util.MixedCollection  
28466      */
28467     fields : false,
28468     
28469     /**
28470      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28471      * Note: the field should not have been rendered yet. For a field that has already been
28472      * rendered, use {@link #addElement}.
28473      * @param {Roo.form.Field} field
28474      * @return {Roo.ToolbarItem}
28475      */
28476      
28477       
28478     addField : function(field) {
28479         if (!this.fields) {
28480             var autoId = 0;
28481             this.fields = new Roo.util.MixedCollection(false, function(o){
28482                 return o.id || ("item" + (++autoId));
28483             });
28484
28485         }
28486         
28487         var td = this.nextBlock();
28488         field.render(td);
28489         var ti = new Roo.Toolbar.Item(td.firstChild);
28490         ti.render(td);
28491         this.items.add(ti);
28492         this.fields.add(field);
28493         return ti;
28494     },
28495     /**
28496      * Hide the toolbar
28497      * @method hide
28498      */
28499      
28500       
28501     hide : function()
28502     {
28503         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28504         this.el.child('div').hide();
28505     },
28506     /**
28507      * Show the toolbar
28508      * @method show
28509      */
28510     show : function()
28511     {
28512         this.el.child('div').show();
28513     },
28514       
28515     // private
28516     nextBlock : function(){
28517         var td = document.createElement("td");
28518         this.tr.appendChild(td);
28519         return td;
28520     },
28521
28522     // private
28523     destroy : function(){
28524         if(this.items){ // rendered?
28525             Roo.destroy.apply(Roo, this.items.items);
28526         }
28527         if(this.fields){ // rendered?
28528             Roo.destroy.apply(Roo, this.fields.items);
28529         }
28530         Roo.Element.uncache(this.el, this.tr);
28531     }
28532 };
28533
28534 /**
28535  * @class Roo.Toolbar.Item
28536  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28537  * @constructor
28538  * Creates a new Item
28539  * @param {HTMLElement} el 
28540  */
28541 Roo.Toolbar.Item = function(el){
28542     var cfg = {};
28543     if (typeof (el.xtype) != 'undefined') {
28544         cfg = el;
28545         el = cfg.el;
28546     }
28547     
28548     this.el = Roo.getDom(el);
28549     this.id = Roo.id(this.el);
28550     this.hidden = false;
28551     
28552     this.addEvents({
28553          /**
28554              * @event render
28555              * Fires when the button is rendered
28556              * @param {Button} this
28557              */
28558         'render': true
28559     });
28560     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28561 };
28562 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28563 //Roo.Toolbar.Item.prototype = {
28564     
28565     /**
28566      * Get this item's HTML Element
28567      * @return {HTMLElement}
28568      */
28569     getEl : function(){
28570        return this.el;  
28571     },
28572
28573     // private
28574     render : function(td){
28575         
28576          this.td = td;
28577         td.appendChild(this.el);
28578         
28579         this.fireEvent('render', this);
28580     },
28581     
28582     /**
28583      * Removes and destroys this item.
28584      */
28585     destroy : function(){
28586         this.td.parentNode.removeChild(this.td);
28587     },
28588     
28589     /**
28590      * Shows this item.
28591      */
28592     show: function(){
28593         this.hidden = false;
28594         this.td.style.display = "";
28595     },
28596     
28597     /**
28598      * Hides this item.
28599      */
28600     hide: function(){
28601         this.hidden = true;
28602         this.td.style.display = "none";
28603     },
28604     
28605     /**
28606      * Convenience function for boolean show/hide.
28607      * @param {Boolean} visible true to show/false to hide
28608      */
28609     setVisible: function(visible){
28610         if(visible) {
28611             this.show();
28612         }else{
28613             this.hide();
28614         }
28615     },
28616     
28617     /**
28618      * Try to focus this item.
28619      */
28620     focus : function(){
28621         Roo.fly(this.el).focus();
28622     },
28623     
28624     /**
28625      * Disables this item.
28626      */
28627     disable : function(){
28628         Roo.fly(this.td).addClass("x-item-disabled");
28629         this.disabled = true;
28630         this.el.disabled = true;
28631     },
28632     
28633     /**
28634      * Enables this item.
28635      */
28636     enable : function(){
28637         Roo.fly(this.td).removeClass("x-item-disabled");
28638         this.disabled = false;
28639         this.el.disabled = false;
28640     }
28641 });
28642
28643
28644 /**
28645  * @class Roo.Toolbar.Separator
28646  * @extends Roo.Toolbar.Item
28647  * A simple toolbar separator class
28648  * @constructor
28649  * Creates a new Separator
28650  */
28651 Roo.Toolbar.Separator = function(cfg){
28652     
28653     var s = document.createElement("span");
28654     s.className = "ytb-sep";
28655     if (cfg) {
28656         cfg.el = s;
28657     }
28658     
28659     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28660 };
28661 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28662     enable:Roo.emptyFn,
28663     disable:Roo.emptyFn,
28664     focus:Roo.emptyFn
28665 });
28666
28667 /**
28668  * @class Roo.Toolbar.Spacer
28669  * @extends Roo.Toolbar.Item
28670  * A simple element that adds extra horizontal space to a toolbar.
28671  * @constructor
28672  * Creates a new Spacer
28673  */
28674 Roo.Toolbar.Spacer = function(cfg){
28675     var s = document.createElement("div");
28676     s.className = "ytb-spacer";
28677     if (cfg) {
28678         cfg.el = s;
28679     }
28680     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28681 };
28682 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28683     enable:Roo.emptyFn,
28684     disable:Roo.emptyFn,
28685     focus:Roo.emptyFn
28686 });
28687
28688 /**
28689  * @class Roo.Toolbar.Fill
28690  * @extends Roo.Toolbar.Spacer
28691  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28692  * @constructor
28693  * Creates a new Spacer
28694  */
28695 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28696     // private
28697     render : function(td){
28698         td.style.width = '100%';
28699         Roo.Toolbar.Fill.superclass.render.call(this, td);
28700     }
28701 });
28702
28703 /**
28704  * @class Roo.Toolbar.TextItem
28705  * @extends Roo.Toolbar.Item
28706  * A simple class that renders text directly into a toolbar.
28707  * @constructor
28708  * Creates a new TextItem
28709  * @param {String} text
28710  */
28711 Roo.Toolbar.TextItem = function(cfg){
28712     var  text = cfg || "";
28713     if (typeof(cfg) == 'object') {
28714         text = cfg.text || "";
28715     }  else {
28716         cfg = null;
28717     }
28718     var s = document.createElement("span");
28719     s.className = "ytb-text";
28720     s.innerHTML = text;
28721     if (cfg) {
28722         cfg.el  = s;
28723     }
28724     
28725     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28726 };
28727 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28728     
28729      
28730     enable:Roo.emptyFn,
28731     disable:Roo.emptyFn,
28732     focus:Roo.emptyFn
28733 });
28734
28735 /**
28736  * @class Roo.Toolbar.Button
28737  * @extends Roo.Button
28738  * A button that renders into a toolbar.
28739  * @constructor
28740  * Creates a new Button
28741  * @param {Object} config A standard {@link Roo.Button} config object
28742  */
28743 Roo.Toolbar.Button = function(config){
28744     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28745 };
28746 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28747     render : function(td){
28748         this.td = td;
28749         Roo.Toolbar.Button.superclass.render.call(this, td);
28750     },
28751     
28752     /**
28753      * Removes and destroys this button
28754      */
28755     destroy : function(){
28756         Roo.Toolbar.Button.superclass.destroy.call(this);
28757         this.td.parentNode.removeChild(this.td);
28758     },
28759     
28760     /**
28761      * Shows this button
28762      */
28763     show: function(){
28764         this.hidden = false;
28765         this.td.style.display = "";
28766     },
28767     
28768     /**
28769      * Hides this button
28770      */
28771     hide: function(){
28772         this.hidden = true;
28773         this.td.style.display = "none";
28774     },
28775
28776     /**
28777      * Disables this item
28778      */
28779     disable : function(){
28780         Roo.fly(this.td).addClass("x-item-disabled");
28781         this.disabled = true;
28782     },
28783
28784     /**
28785      * Enables this item
28786      */
28787     enable : function(){
28788         Roo.fly(this.td).removeClass("x-item-disabled");
28789         this.disabled = false;
28790     }
28791 });
28792 // backwards compat
28793 Roo.ToolbarButton = Roo.Toolbar.Button;
28794
28795 /**
28796  * @class Roo.Toolbar.SplitButton
28797  * @extends Roo.SplitButton
28798  * A menu button that renders into a toolbar.
28799  * @constructor
28800  * Creates a new SplitButton
28801  * @param {Object} config A standard {@link Roo.SplitButton} config object
28802  */
28803 Roo.Toolbar.SplitButton = function(config){
28804     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28805 };
28806 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28807     render : function(td){
28808         this.td = td;
28809         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28810     },
28811     
28812     /**
28813      * Removes and destroys this button
28814      */
28815     destroy : function(){
28816         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28817         this.td.parentNode.removeChild(this.td);
28818     },
28819     
28820     /**
28821      * Shows this button
28822      */
28823     show: function(){
28824         this.hidden = false;
28825         this.td.style.display = "";
28826     },
28827     
28828     /**
28829      * Hides this button
28830      */
28831     hide: function(){
28832         this.hidden = true;
28833         this.td.style.display = "none";
28834     }
28835 });
28836
28837 // backwards compat
28838 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28839  * Based on:
28840  * Ext JS Library 1.1.1
28841  * Copyright(c) 2006-2007, Ext JS, LLC.
28842  *
28843  * Originally Released Under LGPL - original licence link has changed is not relivant.
28844  *
28845  * Fork - LGPL
28846  * <script type="text/javascript">
28847  */
28848  
28849 /**
28850  * @class Roo.PagingToolbar
28851  * @extends Roo.Toolbar
28852  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28853  * @constructor
28854  * Create a new PagingToolbar
28855  * @param {Object} config The config object
28856  */
28857 Roo.PagingToolbar = function(el, ds, config)
28858 {
28859     // old args format still supported... - xtype is prefered..
28860     if (typeof(el) == 'object' && el.xtype) {
28861         // created from xtype...
28862         config = el;
28863         ds = el.dataSource;
28864         el = config.container;
28865     }
28866     var items = [];
28867     if (config.items) {
28868         items = config.items;
28869         config.items = [];
28870     }
28871     
28872     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28873     this.ds = ds;
28874     this.cursor = 0;
28875     this.renderButtons(this.el);
28876     this.bind(ds);
28877     
28878     // supprot items array.
28879    
28880     Roo.each(items, function(e) {
28881         this.add(Roo.factory(e));
28882     },this);
28883     
28884 };
28885
28886 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28887     /**
28888      * @cfg {Roo.data.Store} dataSource
28889      * The underlying data store providing the paged data
28890      */
28891     /**
28892      * @cfg {String/HTMLElement/Element} container
28893      * container The id or element that will contain the toolbar
28894      */
28895     /**
28896      * @cfg {Boolean} displayInfo
28897      * True to display the displayMsg (defaults to false)
28898      */
28899     /**
28900      * @cfg {Number} pageSize
28901      * The number of records to display per page (defaults to 20)
28902      */
28903     pageSize: 20,
28904     /**
28905      * @cfg {String} displayMsg
28906      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28907      */
28908     displayMsg : 'Displaying {0} - {1} of {2}',
28909     /**
28910      * @cfg {String} emptyMsg
28911      * The message to display when no records are found (defaults to "No data to display")
28912      */
28913     emptyMsg : 'No data to display',
28914     /**
28915      * Customizable piece of the default paging text (defaults to "Page")
28916      * @type String
28917      */
28918     beforePageText : "Page",
28919     /**
28920      * Customizable piece of the default paging text (defaults to "of %0")
28921      * @type String
28922      */
28923     afterPageText : "of {0}",
28924     /**
28925      * Customizable piece of the default paging text (defaults to "First Page")
28926      * @type String
28927      */
28928     firstText : "First Page",
28929     /**
28930      * Customizable piece of the default paging text (defaults to "Previous Page")
28931      * @type String
28932      */
28933     prevText : "Previous Page",
28934     /**
28935      * Customizable piece of the default paging text (defaults to "Next Page")
28936      * @type String
28937      */
28938     nextText : "Next Page",
28939     /**
28940      * Customizable piece of the default paging text (defaults to "Last Page")
28941      * @type String
28942      */
28943     lastText : "Last Page",
28944     /**
28945      * Customizable piece of the default paging text (defaults to "Refresh")
28946      * @type String
28947      */
28948     refreshText : "Refresh",
28949
28950     // private
28951     renderButtons : function(el){
28952         Roo.PagingToolbar.superclass.render.call(this, el);
28953         this.first = this.addButton({
28954             tooltip: this.firstText,
28955             cls: "x-btn-icon x-grid-page-first",
28956             disabled: true,
28957             handler: this.onClick.createDelegate(this, ["first"])
28958         });
28959         this.prev = this.addButton({
28960             tooltip: this.prevText,
28961             cls: "x-btn-icon x-grid-page-prev",
28962             disabled: true,
28963             handler: this.onClick.createDelegate(this, ["prev"])
28964         });
28965         //this.addSeparator();
28966         this.add(this.beforePageText);
28967         this.field = Roo.get(this.addDom({
28968            tag: "input",
28969            type: "text",
28970            size: "3",
28971            value: "1",
28972            cls: "x-grid-page-number"
28973         }).el);
28974         this.field.on("keydown", this.onPagingKeydown, this);
28975         this.field.on("focus", function(){this.dom.select();});
28976         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28977         this.field.setHeight(18);
28978         //this.addSeparator();
28979         this.next = this.addButton({
28980             tooltip: this.nextText,
28981             cls: "x-btn-icon x-grid-page-next",
28982             disabled: true,
28983             handler: this.onClick.createDelegate(this, ["next"])
28984         });
28985         this.last = this.addButton({
28986             tooltip: this.lastText,
28987             cls: "x-btn-icon x-grid-page-last",
28988             disabled: true,
28989             handler: this.onClick.createDelegate(this, ["last"])
28990         });
28991         //this.addSeparator();
28992         this.loading = this.addButton({
28993             tooltip: this.refreshText,
28994             cls: "x-btn-icon x-grid-loading",
28995             handler: this.onClick.createDelegate(this, ["refresh"])
28996         });
28997
28998         if(this.displayInfo){
28999             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
29000         }
29001     },
29002
29003     // private
29004     updateInfo : function(){
29005         if(this.displayEl){
29006             var count = this.ds.getCount();
29007             var msg = count == 0 ?
29008                 this.emptyMsg :
29009                 String.format(
29010                     this.displayMsg,
29011                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29012                 );
29013             this.displayEl.update(msg);
29014         }
29015     },
29016
29017     // private
29018     onLoad : function(ds, r, o){
29019        this.cursor = o.params ? o.params.start : 0;
29020        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29021
29022        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29023        this.field.dom.value = ap;
29024        this.first.setDisabled(ap == 1);
29025        this.prev.setDisabled(ap == 1);
29026        this.next.setDisabled(ap == ps);
29027        this.last.setDisabled(ap == ps);
29028        this.loading.enable();
29029        this.updateInfo();
29030     },
29031
29032     // private
29033     getPageData : function(){
29034         var total = this.ds.getTotalCount();
29035         return {
29036             total : total,
29037             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29038             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29039         };
29040     },
29041
29042     // private
29043     onLoadError : function(){
29044         this.loading.enable();
29045     },
29046
29047     // private
29048     onPagingKeydown : function(e){
29049         var k = e.getKey();
29050         var d = this.getPageData();
29051         if(k == e.RETURN){
29052             var v = this.field.dom.value, pageNum;
29053             if(!v || isNaN(pageNum = parseInt(v, 10))){
29054                 this.field.dom.value = d.activePage;
29055                 return;
29056             }
29057             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29058             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29059             e.stopEvent();
29060         }
29061         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))
29062         {
29063           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29064           this.field.dom.value = pageNum;
29065           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29066           e.stopEvent();
29067         }
29068         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29069         {
29070           var v = this.field.dom.value, pageNum; 
29071           var increment = (e.shiftKey) ? 10 : 1;
29072           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29073             increment *= -1;
29074           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29075             this.field.dom.value = d.activePage;
29076             return;
29077           }
29078           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29079           {
29080             this.field.dom.value = parseInt(v, 10) + increment;
29081             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29082             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29083           }
29084           e.stopEvent();
29085         }
29086     },
29087
29088     // private
29089     beforeLoad : function(){
29090         if(this.loading){
29091             this.loading.disable();
29092         }
29093     },
29094
29095     // private
29096     onClick : function(which){
29097         var ds = this.ds;
29098         switch(which){
29099             case "first":
29100                 ds.load({params:{start: 0, limit: this.pageSize}});
29101             break;
29102             case "prev":
29103                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29104             break;
29105             case "next":
29106                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29107             break;
29108             case "last":
29109                 var total = ds.getTotalCount();
29110                 var extra = total % this.pageSize;
29111                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29112                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29113             break;
29114             case "refresh":
29115                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29116             break;
29117         }
29118     },
29119
29120     /**
29121      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29122      * @param {Roo.data.Store} store The data store to unbind
29123      */
29124     unbind : function(ds){
29125         ds.un("beforeload", this.beforeLoad, this);
29126         ds.un("load", this.onLoad, this);
29127         ds.un("loadexception", this.onLoadError, this);
29128         ds.un("remove", this.updateInfo, this);
29129         ds.un("add", this.updateInfo, this);
29130         this.ds = undefined;
29131     },
29132
29133     /**
29134      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29135      * @param {Roo.data.Store} store The data store to bind
29136      */
29137     bind : function(ds){
29138         ds.on("beforeload", this.beforeLoad, this);
29139         ds.on("load", this.onLoad, this);
29140         ds.on("loadexception", this.onLoadError, this);
29141         ds.on("remove", this.updateInfo, this);
29142         ds.on("add", this.updateInfo, this);
29143         this.ds = ds;
29144     }
29145 });/*
29146  * Based on:
29147  * Ext JS Library 1.1.1
29148  * Copyright(c) 2006-2007, Ext JS, LLC.
29149  *
29150  * Originally Released Under LGPL - original licence link has changed is not relivant.
29151  *
29152  * Fork - LGPL
29153  * <script type="text/javascript">
29154  */
29155
29156 /**
29157  * @class Roo.Resizable
29158  * @extends Roo.util.Observable
29159  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29160  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29161  * 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
29162  * the element will be wrapped for you automatically.</p>
29163  * <p>Here is the list of valid resize handles:</p>
29164  * <pre>
29165 Value   Description
29166 ------  -------------------
29167  'n'     north
29168  's'     south
29169  'e'     east
29170  'w'     west
29171  'nw'    northwest
29172  'sw'    southwest
29173  'se'    southeast
29174  'ne'    northeast
29175  'hd'    horizontal drag
29176  'all'   all
29177 </pre>
29178  * <p>Here's an example showing the creation of a typical Resizable:</p>
29179  * <pre><code>
29180 var resizer = new Roo.Resizable("element-id", {
29181     handles: 'all',
29182     minWidth: 200,
29183     minHeight: 100,
29184     maxWidth: 500,
29185     maxHeight: 400,
29186     pinned: true
29187 });
29188 resizer.on("resize", myHandler);
29189 </code></pre>
29190  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29191  * resizer.east.setDisplayed(false);</p>
29192  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29193  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29194  * resize operation's new size (defaults to [0, 0])
29195  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29196  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29197  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29198  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29199  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29200  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29201  * @cfg {Number} width The width of the element in pixels (defaults to null)
29202  * @cfg {Number} height The height of the element in pixels (defaults to null)
29203  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29204  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29205  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29206  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29207  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29208  * in favor of the handles config option (defaults to false)
29209  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29210  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29211  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29212  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29213  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29214  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29215  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29216  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29217  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29218  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29219  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29220  * @constructor
29221  * Create a new resizable component
29222  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29223  * @param {Object} config configuration options
29224   */
29225 Roo.Resizable = function(el, config)
29226 {
29227     this.el = Roo.get(el);
29228
29229     if(config && config.wrap){
29230         config.resizeChild = this.el;
29231         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29232         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29233         this.el.setStyle("overflow", "hidden");
29234         this.el.setPositioning(config.resizeChild.getPositioning());
29235         config.resizeChild.clearPositioning();
29236         if(!config.width || !config.height){
29237             var csize = config.resizeChild.getSize();
29238             this.el.setSize(csize.width, csize.height);
29239         }
29240         if(config.pinned && !config.adjustments){
29241             config.adjustments = "auto";
29242         }
29243     }
29244
29245     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29246     this.proxy.unselectable();
29247     this.proxy.enableDisplayMode('block');
29248
29249     Roo.apply(this, config);
29250
29251     if(this.pinned){
29252         this.disableTrackOver = true;
29253         this.el.addClass("x-resizable-pinned");
29254     }
29255     // if the element isn't positioned, make it relative
29256     var position = this.el.getStyle("position");
29257     if(position != "absolute" && position != "fixed"){
29258         this.el.setStyle("position", "relative");
29259     }
29260     if(!this.handles){ // no handles passed, must be legacy style
29261         this.handles = 's,e,se';
29262         if(this.multiDirectional){
29263             this.handles += ',n,w';
29264         }
29265     }
29266     if(this.handles == "all"){
29267         this.handles = "n s e w ne nw se sw";
29268     }
29269     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29270     var ps = Roo.Resizable.positions;
29271     for(var i = 0, len = hs.length; i < len; i++){
29272         if(hs[i] && ps[hs[i]]){
29273             var pos = ps[hs[i]];
29274             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29275         }
29276     }
29277     // legacy
29278     this.corner = this.southeast;
29279     
29280     // updateBox = the box can move..
29281     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29282         this.updateBox = true;
29283     }
29284
29285     this.activeHandle = null;
29286
29287     if(this.resizeChild){
29288         if(typeof this.resizeChild == "boolean"){
29289             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29290         }else{
29291             this.resizeChild = Roo.get(this.resizeChild, true);
29292         }
29293     }
29294     
29295     if(this.adjustments == "auto"){
29296         var rc = this.resizeChild;
29297         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29298         if(rc && (hw || hn)){
29299             rc.position("relative");
29300             rc.setLeft(hw ? hw.el.getWidth() : 0);
29301             rc.setTop(hn ? hn.el.getHeight() : 0);
29302         }
29303         this.adjustments = [
29304             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29305             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29306         ];
29307     }
29308
29309     if(this.draggable){
29310         this.dd = this.dynamic ?
29311             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29312         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29313     }
29314
29315     // public events
29316     this.addEvents({
29317         /**
29318          * @event beforeresize
29319          * Fired before resize is allowed. Set enabled to false to cancel resize.
29320          * @param {Roo.Resizable} this
29321          * @param {Roo.EventObject} e The mousedown event
29322          */
29323         "beforeresize" : true,
29324         /**
29325          * @event resizing
29326          * Fired a resizing.
29327          * @param {Roo.Resizable} this
29328          * @param {Number} x The new x position
29329          * @param {Number} y The new y position
29330          * @param {Number} w The new w width
29331          * @param {Number} h The new h hight
29332          * @param {Roo.EventObject} e The mouseup event
29333          */
29334         "resizing" : true,
29335         /**
29336          * @event resize
29337          * Fired after a resize.
29338          * @param {Roo.Resizable} this
29339          * @param {Number} width The new width
29340          * @param {Number} height The new height
29341          * @param {Roo.EventObject} e The mouseup event
29342          */
29343         "resize" : true
29344     });
29345
29346     if(this.width !== null && this.height !== null){
29347         this.resizeTo(this.width, this.height);
29348     }else{
29349         this.updateChildSize();
29350     }
29351     if(Roo.isIE){
29352         this.el.dom.style.zoom = 1;
29353     }
29354     Roo.Resizable.superclass.constructor.call(this);
29355 };
29356
29357 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29358         resizeChild : false,
29359         adjustments : [0, 0],
29360         minWidth : 5,
29361         minHeight : 5,
29362         maxWidth : 10000,
29363         maxHeight : 10000,
29364         enabled : true,
29365         animate : false,
29366         duration : .35,
29367         dynamic : false,
29368         handles : false,
29369         multiDirectional : false,
29370         disableTrackOver : false,
29371         easing : 'easeOutStrong',
29372         widthIncrement : 0,
29373         heightIncrement : 0,
29374         pinned : false,
29375         width : null,
29376         height : null,
29377         preserveRatio : false,
29378         transparent: false,
29379         minX: 0,
29380         minY: 0,
29381         draggable: false,
29382
29383         /**
29384          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29385          */
29386         constrainTo: undefined,
29387         /**
29388          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29389          */
29390         resizeRegion: undefined,
29391
29392
29393     /**
29394      * Perform a manual resize
29395      * @param {Number} width
29396      * @param {Number} height
29397      */
29398     resizeTo : function(width, height){
29399         this.el.setSize(width, height);
29400         this.updateChildSize();
29401         this.fireEvent("resize", this, width, height, null);
29402     },
29403
29404     // private
29405     startSizing : function(e, handle){
29406         this.fireEvent("beforeresize", this, e);
29407         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29408
29409             if(!this.overlay){
29410                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29411                 this.overlay.unselectable();
29412                 this.overlay.enableDisplayMode("block");
29413                 this.overlay.on("mousemove", this.onMouseMove, this);
29414                 this.overlay.on("mouseup", this.onMouseUp, this);
29415             }
29416             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29417
29418             this.resizing = true;
29419             this.startBox = this.el.getBox();
29420             this.startPoint = e.getXY();
29421             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29422                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29423
29424             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29425             this.overlay.show();
29426
29427             if(this.constrainTo) {
29428                 var ct = Roo.get(this.constrainTo);
29429                 this.resizeRegion = ct.getRegion().adjust(
29430                     ct.getFrameWidth('t'),
29431                     ct.getFrameWidth('l'),
29432                     -ct.getFrameWidth('b'),
29433                     -ct.getFrameWidth('r')
29434                 );
29435             }
29436
29437             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29438             this.proxy.show();
29439             this.proxy.setBox(this.startBox);
29440             if(!this.dynamic){
29441                 this.proxy.setStyle('visibility', 'visible');
29442             }
29443         }
29444     },
29445
29446     // private
29447     onMouseDown : function(handle, e){
29448         if(this.enabled){
29449             e.stopEvent();
29450             this.activeHandle = handle;
29451             this.startSizing(e, handle);
29452         }
29453     },
29454
29455     // private
29456     onMouseUp : function(e){
29457         var size = this.resizeElement();
29458         this.resizing = false;
29459         this.handleOut();
29460         this.overlay.hide();
29461         this.proxy.hide();
29462         this.fireEvent("resize", this, size.width, size.height, e);
29463     },
29464
29465     // private
29466     updateChildSize : function(){
29467         
29468         if(this.resizeChild){
29469             var el = this.el;
29470             var child = this.resizeChild;
29471             var adj = this.adjustments;
29472             if(el.dom.offsetWidth){
29473                 var b = el.getSize(true);
29474                 child.setSize(b.width+adj[0], b.height+adj[1]);
29475             }
29476             // Second call here for IE
29477             // The first call enables instant resizing and
29478             // the second call corrects scroll bars if they
29479             // exist
29480             if(Roo.isIE){
29481                 setTimeout(function(){
29482                     if(el.dom.offsetWidth){
29483                         var b = el.getSize(true);
29484                         child.setSize(b.width+adj[0], b.height+adj[1]);
29485                     }
29486                 }, 10);
29487             }
29488         }
29489     },
29490
29491     // private
29492     snap : function(value, inc, min){
29493         if(!inc || !value) return value;
29494         var newValue = value;
29495         var m = value % inc;
29496         if(m > 0){
29497             if(m > (inc/2)){
29498                 newValue = value + (inc-m);
29499             }else{
29500                 newValue = value - m;
29501             }
29502         }
29503         return Math.max(min, newValue);
29504     },
29505
29506     // private
29507     resizeElement : function(){
29508         var box = this.proxy.getBox();
29509         if(this.updateBox){
29510             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29511         }else{
29512             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29513         }
29514         this.updateChildSize();
29515         if(!this.dynamic){
29516             this.proxy.hide();
29517         }
29518         return box;
29519     },
29520
29521     // private
29522     constrain : function(v, diff, m, mx){
29523         if(v - diff < m){
29524             diff = v - m;
29525         }else if(v - diff > mx){
29526             diff = mx - v;
29527         }
29528         return diff;
29529     },
29530
29531     // private
29532     onMouseMove : function(e){
29533         
29534         if(this.enabled){
29535             try{// try catch so if something goes wrong the user doesn't get hung
29536
29537             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29538                 return;
29539             }
29540
29541             //var curXY = this.startPoint;
29542             var curSize = this.curSize || this.startBox;
29543             var x = this.startBox.x, y = this.startBox.y;
29544             var ox = x, oy = y;
29545             var w = curSize.width, h = curSize.height;
29546             var ow = w, oh = h;
29547             var mw = this.minWidth, mh = this.minHeight;
29548             var mxw = this.maxWidth, mxh = this.maxHeight;
29549             var wi = this.widthIncrement;
29550             var hi = this.heightIncrement;
29551
29552             var eventXY = e.getXY();
29553             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29554             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29555
29556             var pos = this.activeHandle.position;
29557
29558             switch(pos){
29559                 case "east":
29560                     w += diffX;
29561                     w = Math.min(Math.max(mw, w), mxw);
29562                     break;
29563              
29564                 case "south":
29565                     h += diffY;
29566                     h = Math.min(Math.max(mh, h), mxh);
29567                     break;
29568                 case "southeast":
29569                     w += diffX;
29570                     h += diffY;
29571                     w = Math.min(Math.max(mw, w), mxw);
29572                     h = Math.min(Math.max(mh, h), mxh);
29573                     break;
29574                 case "north":
29575                     diffY = this.constrain(h, diffY, mh, mxh);
29576                     y += diffY;
29577                     h -= diffY;
29578                     break;
29579                 case "hdrag":
29580                     
29581                     if (wi) {
29582                         var adiffX = Math.abs(diffX);
29583                         var sub = (adiffX % wi); // how much 
29584                         if (sub > (wi/2)) { // far enough to snap
29585                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29586                         } else {
29587                             // remove difference.. 
29588                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29589                         }
29590                     }
29591                     x += diffX;
29592                     x = Math.max(this.minX, x);
29593                     break;
29594                 case "west":
29595                     diffX = this.constrain(w, diffX, mw, mxw);
29596                     x += diffX;
29597                     w -= diffX;
29598                     break;
29599                 case "northeast":
29600                     w += diffX;
29601                     w = Math.min(Math.max(mw, w), mxw);
29602                     diffY = this.constrain(h, diffY, mh, mxh);
29603                     y += diffY;
29604                     h -= diffY;
29605                     break;
29606                 case "northwest":
29607                     diffX = this.constrain(w, diffX, mw, mxw);
29608                     diffY = this.constrain(h, diffY, mh, mxh);
29609                     y += diffY;
29610                     h -= diffY;
29611                     x += diffX;
29612                     w -= diffX;
29613                     break;
29614                case "southwest":
29615                     diffX = this.constrain(w, diffX, mw, mxw);
29616                     h += diffY;
29617                     h = Math.min(Math.max(mh, h), mxh);
29618                     x += diffX;
29619                     w -= diffX;
29620                     break;
29621             }
29622
29623             var sw = this.snap(w, wi, mw);
29624             var sh = this.snap(h, hi, mh);
29625             if(sw != w || sh != h){
29626                 switch(pos){
29627                     case "northeast":
29628                         y -= sh - h;
29629                     break;
29630                     case "north":
29631                         y -= sh - h;
29632                         break;
29633                     case "southwest":
29634                         x -= sw - w;
29635                     break;
29636                     case "west":
29637                         x -= sw - w;
29638                         break;
29639                     case "northwest":
29640                         x -= sw - w;
29641                         y -= sh - h;
29642                     break;
29643                 }
29644                 w = sw;
29645                 h = sh;
29646             }
29647
29648             if(this.preserveRatio){
29649                 switch(pos){
29650                     case "southeast":
29651                     case "east":
29652                         h = oh * (w/ow);
29653                         h = Math.min(Math.max(mh, h), mxh);
29654                         w = ow * (h/oh);
29655                        break;
29656                     case "south":
29657                         w = ow * (h/oh);
29658                         w = Math.min(Math.max(mw, w), mxw);
29659                         h = oh * (w/ow);
29660                         break;
29661                     case "northeast":
29662                         w = ow * (h/oh);
29663                         w = Math.min(Math.max(mw, w), mxw);
29664                         h = oh * (w/ow);
29665                     break;
29666                     case "north":
29667                         var tw = w;
29668                         w = ow * (h/oh);
29669                         w = Math.min(Math.max(mw, w), mxw);
29670                         h = oh * (w/ow);
29671                         x += (tw - w) / 2;
29672                         break;
29673                     case "southwest":
29674                         h = oh * (w/ow);
29675                         h = Math.min(Math.max(mh, h), mxh);
29676                         var tw = w;
29677                         w = ow * (h/oh);
29678                         x += tw - w;
29679                         break;
29680                     case "west":
29681                         var th = h;
29682                         h = oh * (w/ow);
29683                         h = Math.min(Math.max(mh, h), mxh);
29684                         y += (th - h) / 2;
29685                         var tw = w;
29686                         w = ow * (h/oh);
29687                         x += tw - w;
29688                        break;
29689                     case "northwest":
29690                         var tw = w;
29691                         var th = h;
29692                         h = oh * (w/ow);
29693                         h = Math.min(Math.max(mh, h), mxh);
29694                         w = ow * (h/oh);
29695                         y += th - h;
29696                         x += tw - w;
29697                        break;
29698
29699                 }
29700             }
29701             if (pos == 'hdrag') {
29702                 w = ow;
29703             }
29704             this.proxy.setBounds(x, y, w, h);
29705             if(this.dynamic){
29706                 this.resizeElement();
29707             }
29708             }catch(e){}
29709         }
29710         this.fireEvent("resizing", this, x, y, w, h, e);
29711     },
29712
29713     // private
29714     handleOver : function(){
29715         if(this.enabled){
29716             this.el.addClass("x-resizable-over");
29717         }
29718     },
29719
29720     // private
29721     handleOut : function(){
29722         if(!this.resizing){
29723             this.el.removeClass("x-resizable-over");
29724         }
29725     },
29726
29727     /**
29728      * Returns the element this component is bound to.
29729      * @return {Roo.Element}
29730      */
29731     getEl : function(){
29732         return this.el;
29733     },
29734
29735     /**
29736      * Returns the resizeChild element (or null).
29737      * @return {Roo.Element}
29738      */
29739     getResizeChild : function(){
29740         return this.resizeChild;
29741     },
29742     groupHandler : function()
29743     {
29744         
29745     },
29746     /**
29747      * Destroys this resizable. If the element was wrapped and
29748      * removeEl is not true then the element remains.
29749      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29750      */
29751     destroy : function(removeEl){
29752         this.proxy.remove();
29753         if(this.overlay){
29754             this.overlay.removeAllListeners();
29755             this.overlay.remove();
29756         }
29757         var ps = Roo.Resizable.positions;
29758         for(var k in ps){
29759             if(typeof ps[k] != "function" && this[ps[k]]){
29760                 var h = this[ps[k]];
29761                 h.el.removeAllListeners();
29762                 h.el.remove();
29763             }
29764         }
29765         if(removeEl){
29766             this.el.update("");
29767             this.el.remove();
29768         }
29769     }
29770 });
29771
29772 // private
29773 // hash to map config positions to true positions
29774 Roo.Resizable.positions = {
29775     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29776     hd: "hdrag"
29777 };
29778
29779 // private
29780 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29781     if(!this.tpl){
29782         // only initialize the template if resizable is used
29783         var tpl = Roo.DomHelper.createTemplate(
29784             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29785         );
29786         tpl.compile();
29787         Roo.Resizable.Handle.prototype.tpl = tpl;
29788     }
29789     this.position = pos;
29790     this.rz = rz;
29791     // show north drag fro topdra
29792     var handlepos = pos == 'hdrag' ? 'north' : pos;
29793     
29794     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29795     if (pos == 'hdrag') {
29796         this.el.setStyle('cursor', 'pointer');
29797     }
29798     this.el.unselectable();
29799     if(transparent){
29800         this.el.setOpacity(0);
29801     }
29802     this.el.on("mousedown", this.onMouseDown, this);
29803     if(!disableTrackOver){
29804         this.el.on("mouseover", this.onMouseOver, this);
29805         this.el.on("mouseout", this.onMouseOut, this);
29806     }
29807 };
29808
29809 // private
29810 Roo.Resizable.Handle.prototype = {
29811     afterResize : function(rz){
29812         Roo.log('after?');
29813         // do nothing
29814     },
29815     // private
29816     onMouseDown : function(e){
29817         this.rz.onMouseDown(this, e);
29818     },
29819     // private
29820     onMouseOver : function(e){
29821         this.rz.handleOver(this, e);
29822     },
29823     // private
29824     onMouseOut : function(e){
29825         this.rz.handleOut(this, e);
29826     }
29827 };/*
29828  * Based on:
29829  * Ext JS Library 1.1.1
29830  * Copyright(c) 2006-2007, Ext JS, LLC.
29831  *
29832  * Originally Released Under LGPL - original licence link has changed is not relivant.
29833  *
29834  * Fork - LGPL
29835  * <script type="text/javascript">
29836  */
29837
29838 /**
29839  * @class Roo.Editor
29840  * @extends Roo.Component
29841  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29842  * @constructor
29843  * Create a new Editor
29844  * @param {Roo.form.Field} field The Field object (or descendant)
29845  * @param {Object} config The config object
29846  */
29847 Roo.Editor = function(field, config){
29848     Roo.Editor.superclass.constructor.call(this, config);
29849     this.field = field;
29850     this.addEvents({
29851         /**
29852              * @event beforestartedit
29853              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29854              * false from the handler of this event.
29855              * @param {Editor} this
29856              * @param {Roo.Element} boundEl The underlying element bound to this editor
29857              * @param {Mixed} value The field value being set
29858              */
29859         "beforestartedit" : true,
29860         /**
29861              * @event startedit
29862              * Fires when this editor is displayed
29863              * @param {Roo.Element} boundEl The underlying element bound to this editor
29864              * @param {Mixed} value The starting field value
29865              */
29866         "startedit" : true,
29867         /**
29868              * @event beforecomplete
29869              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29870              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29871              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29872              * event will not fire since no edit actually occurred.
29873              * @param {Editor} this
29874              * @param {Mixed} value The current field value
29875              * @param {Mixed} startValue The original field value
29876              */
29877         "beforecomplete" : true,
29878         /**
29879              * @event complete
29880              * Fires after editing is complete and any changed value has been written to the underlying field.
29881              * @param {Editor} this
29882              * @param {Mixed} value The current field value
29883              * @param {Mixed} startValue The original field value
29884              */
29885         "complete" : true,
29886         /**
29887          * @event specialkey
29888          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29889          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29890          * @param {Roo.form.Field} this
29891          * @param {Roo.EventObject} e The event object
29892          */
29893         "specialkey" : true
29894     });
29895 };
29896
29897 Roo.extend(Roo.Editor, Roo.Component, {
29898     /**
29899      * @cfg {Boolean/String} autosize
29900      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29901      * or "height" to adopt the height only (defaults to false)
29902      */
29903     /**
29904      * @cfg {Boolean} revertInvalid
29905      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29906      * validation fails (defaults to true)
29907      */
29908     /**
29909      * @cfg {Boolean} ignoreNoChange
29910      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29911      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29912      * will never be ignored.
29913      */
29914     /**
29915      * @cfg {Boolean} hideEl
29916      * False to keep the bound element visible while the editor is displayed (defaults to true)
29917      */
29918     /**
29919      * @cfg {Mixed} value
29920      * The data value of the underlying field (defaults to "")
29921      */
29922     value : "",
29923     /**
29924      * @cfg {String} alignment
29925      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29926      */
29927     alignment: "c-c?",
29928     /**
29929      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29930      * for bottom-right shadow (defaults to "frame")
29931      */
29932     shadow : "frame",
29933     /**
29934      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29935      */
29936     constrain : false,
29937     /**
29938      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29939      */
29940     completeOnEnter : false,
29941     /**
29942      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29943      */
29944     cancelOnEsc : false,
29945     /**
29946      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29947      */
29948     updateEl : false,
29949
29950     // private
29951     onRender : function(ct, position){
29952         this.el = new Roo.Layer({
29953             shadow: this.shadow,
29954             cls: "x-editor",
29955             parentEl : ct,
29956             shim : this.shim,
29957             shadowOffset:4,
29958             id: this.id,
29959             constrain: this.constrain
29960         });
29961         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29962         if(this.field.msgTarget != 'title'){
29963             this.field.msgTarget = 'qtip';
29964         }
29965         this.field.render(this.el);
29966         if(Roo.isGecko){
29967             this.field.el.dom.setAttribute('autocomplete', 'off');
29968         }
29969         this.field.on("specialkey", this.onSpecialKey, this);
29970         if(this.swallowKeys){
29971             this.field.el.swallowEvent(['keydown','keypress']);
29972         }
29973         this.field.show();
29974         this.field.on("blur", this.onBlur, this);
29975         if(this.field.grow){
29976             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29977         }
29978     },
29979
29980     onSpecialKey : function(field, e)
29981     {
29982         //Roo.log('editor onSpecialKey');
29983         if(this.completeOnEnter && e.getKey() == e.ENTER){
29984             e.stopEvent();
29985             this.completeEdit();
29986             return;
29987         }
29988         // do not fire special key otherwise it might hide close the editor...
29989         if(e.getKey() == e.ENTER){    
29990             return;
29991         }
29992         if(this.cancelOnEsc && e.getKey() == e.ESC){
29993             this.cancelEdit();
29994             return;
29995         } 
29996         this.fireEvent('specialkey', field, e);
29997     
29998     },
29999
30000     /**
30001      * Starts the editing process and shows the editor.
30002      * @param {String/HTMLElement/Element} el The element to edit
30003      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30004       * to the innerHTML of el.
30005      */
30006     startEdit : function(el, value){
30007         if(this.editing){
30008             this.completeEdit();
30009         }
30010         this.boundEl = Roo.get(el);
30011         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30012         if(!this.rendered){
30013             this.render(this.parentEl || document.body);
30014         }
30015         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30016             return;
30017         }
30018         this.startValue = v;
30019         this.field.setValue(v);
30020         if(this.autoSize){
30021             var sz = this.boundEl.getSize();
30022             switch(this.autoSize){
30023                 case "width":
30024                 this.setSize(sz.width,  "");
30025                 break;
30026                 case "height":
30027                 this.setSize("",  sz.height);
30028                 break;
30029                 default:
30030                 this.setSize(sz.width,  sz.height);
30031             }
30032         }
30033         this.el.alignTo(this.boundEl, this.alignment);
30034         this.editing = true;
30035         if(Roo.QuickTips){
30036             Roo.QuickTips.disable();
30037         }
30038         this.show();
30039     },
30040
30041     /**
30042      * Sets the height and width of this editor.
30043      * @param {Number} width The new width
30044      * @param {Number} height The new height
30045      */
30046     setSize : function(w, h){
30047         this.field.setSize(w, h);
30048         if(this.el){
30049             this.el.sync();
30050         }
30051     },
30052
30053     /**
30054      * Realigns the editor to the bound field based on the current alignment config value.
30055      */
30056     realign : function(){
30057         this.el.alignTo(this.boundEl, this.alignment);
30058     },
30059
30060     /**
30061      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30062      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30063      */
30064     completeEdit : function(remainVisible){
30065         if(!this.editing){
30066             return;
30067         }
30068         var v = this.getValue();
30069         if(this.revertInvalid !== false && !this.field.isValid()){
30070             v = this.startValue;
30071             this.cancelEdit(true);
30072         }
30073         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30074             this.editing = false;
30075             this.hide();
30076             return;
30077         }
30078         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30079             this.editing = false;
30080             if(this.updateEl && this.boundEl){
30081                 this.boundEl.update(v);
30082             }
30083             if(remainVisible !== true){
30084                 this.hide();
30085             }
30086             this.fireEvent("complete", this, v, this.startValue);
30087         }
30088     },
30089
30090     // private
30091     onShow : function(){
30092         this.el.show();
30093         if(this.hideEl !== false){
30094             this.boundEl.hide();
30095         }
30096         this.field.show();
30097         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30098             this.fixIEFocus = true;
30099             this.deferredFocus.defer(50, this);
30100         }else{
30101             this.field.focus();
30102         }
30103         this.fireEvent("startedit", this.boundEl, this.startValue);
30104     },
30105
30106     deferredFocus : function(){
30107         if(this.editing){
30108             this.field.focus();
30109         }
30110     },
30111
30112     /**
30113      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30114      * reverted to the original starting value.
30115      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30116      * cancel (defaults to false)
30117      */
30118     cancelEdit : function(remainVisible){
30119         if(this.editing){
30120             this.setValue(this.startValue);
30121             if(remainVisible !== true){
30122                 this.hide();
30123             }
30124         }
30125     },
30126
30127     // private
30128     onBlur : function(){
30129         if(this.allowBlur !== true && this.editing){
30130             this.completeEdit();
30131         }
30132     },
30133
30134     // private
30135     onHide : function(){
30136         if(this.editing){
30137             this.completeEdit();
30138             return;
30139         }
30140         this.field.blur();
30141         if(this.field.collapse){
30142             this.field.collapse();
30143         }
30144         this.el.hide();
30145         if(this.hideEl !== false){
30146             this.boundEl.show();
30147         }
30148         if(Roo.QuickTips){
30149             Roo.QuickTips.enable();
30150         }
30151     },
30152
30153     /**
30154      * Sets the data value of the editor
30155      * @param {Mixed} value Any valid value supported by the underlying field
30156      */
30157     setValue : function(v){
30158         this.field.setValue(v);
30159     },
30160
30161     /**
30162      * Gets the data value of the editor
30163      * @return {Mixed} The data value
30164      */
30165     getValue : function(){
30166         return this.field.getValue();
30167     }
30168 });/*
30169  * Based on:
30170  * Ext JS Library 1.1.1
30171  * Copyright(c) 2006-2007, Ext JS, LLC.
30172  *
30173  * Originally Released Under LGPL - original licence link has changed is not relivant.
30174  *
30175  * Fork - LGPL
30176  * <script type="text/javascript">
30177  */
30178  
30179 /**
30180  * @class Roo.BasicDialog
30181  * @extends Roo.util.Observable
30182  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30183  * <pre><code>
30184 var dlg = new Roo.BasicDialog("my-dlg", {
30185     height: 200,
30186     width: 300,
30187     minHeight: 100,
30188     minWidth: 150,
30189     modal: true,
30190     proxyDrag: true,
30191     shadow: true
30192 });
30193 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30194 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30195 dlg.addButton('Cancel', dlg.hide, dlg);
30196 dlg.show();
30197 </code></pre>
30198   <b>A Dialog should always be a direct child of the body element.</b>
30199  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30200  * @cfg {String} title Default text to display in the title bar (defaults to null)
30201  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30202  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30203  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30204  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30205  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30206  * (defaults to null with no animation)
30207  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30208  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30209  * property for valid values (defaults to 'all')
30210  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30211  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30212  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30213  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30214  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30215  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30216  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30217  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30218  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30219  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30220  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30221  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30222  * draggable = true (defaults to false)
30223  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30224  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30225  * shadow (defaults to false)
30226  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30227  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30228  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30229  * @cfg {Array} buttons Array of buttons
30230  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30231  * @constructor
30232  * Create a new BasicDialog.
30233  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30234  * @param {Object} config Configuration options
30235  */
30236 Roo.BasicDialog = function(el, config){
30237     this.el = Roo.get(el);
30238     var dh = Roo.DomHelper;
30239     if(!this.el && config && config.autoCreate){
30240         if(typeof config.autoCreate == "object"){
30241             if(!config.autoCreate.id){
30242                 config.autoCreate.id = el;
30243             }
30244             this.el = dh.append(document.body,
30245                         config.autoCreate, true);
30246         }else{
30247             this.el = dh.append(document.body,
30248                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30249         }
30250     }
30251     el = this.el;
30252     el.setDisplayed(true);
30253     el.hide = this.hideAction;
30254     this.id = el.id;
30255     el.addClass("x-dlg");
30256
30257     Roo.apply(this, config);
30258
30259     this.proxy = el.createProxy("x-dlg-proxy");
30260     this.proxy.hide = this.hideAction;
30261     this.proxy.setOpacity(.5);
30262     this.proxy.hide();
30263
30264     if(config.width){
30265         el.setWidth(config.width);
30266     }
30267     if(config.height){
30268         el.setHeight(config.height);
30269     }
30270     this.size = el.getSize();
30271     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30272         this.xy = [config.x,config.y];
30273     }else{
30274         this.xy = el.getCenterXY(true);
30275     }
30276     /** The header element @type Roo.Element */
30277     this.header = el.child("> .x-dlg-hd");
30278     /** The body element @type Roo.Element */
30279     this.body = el.child("> .x-dlg-bd");
30280     /** The footer element @type Roo.Element */
30281     this.footer = el.child("> .x-dlg-ft");
30282
30283     if(!this.header){
30284         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30285     }
30286     if(!this.body){
30287         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30288     }
30289
30290     this.header.unselectable();
30291     if(this.title){
30292         this.header.update(this.title);
30293     }
30294     // this element allows the dialog to be focused for keyboard event
30295     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30296     this.focusEl.swallowEvent("click", true);
30297
30298     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30299
30300     // wrap the body and footer for special rendering
30301     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30302     if(this.footer){
30303         this.bwrap.dom.appendChild(this.footer.dom);
30304     }
30305
30306     this.bg = this.el.createChild({
30307         tag: "div", cls:"x-dlg-bg",
30308         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30309     });
30310     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30311
30312
30313     if(this.autoScroll !== false && !this.autoTabs){
30314         this.body.setStyle("overflow", "auto");
30315     }
30316
30317     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30318
30319     if(this.closable !== false){
30320         this.el.addClass("x-dlg-closable");
30321         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30322         this.close.on("click", this.closeClick, this);
30323         this.close.addClassOnOver("x-dlg-close-over");
30324     }
30325     if(this.collapsible !== false){
30326         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30327         this.collapseBtn.on("click", this.collapseClick, this);
30328         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30329         this.header.on("dblclick", this.collapseClick, this);
30330     }
30331     if(this.resizable !== false){
30332         this.el.addClass("x-dlg-resizable");
30333         this.resizer = new Roo.Resizable(el, {
30334             minWidth: this.minWidth || 80,
30335             minHeight:this.minHeight || 80,
30336             handles: this.resizeHandles || "all",
30337             pinned: true
30338         });
30339         this.resizer.on("beforeresize", this.beforeResize, this);
30340         this.resizer.on("resize", this.onResize, this);
30341     }
30342     if(this.draggable !== false){
30343         el.addClass("x-dlg-draggable");
30344         if (!this.proxyDrag) {
30345             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30346         }
30347         else {
30348             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30349         }
30350         dd.setHandleElId(this.header.id);
30351         dd.endDrag = this.endMove.createDelegate(this);
30352         dd.startDrag = this.startMove.createDelegate(this);
30353         dd.onDrag = this.onDrag.createDelegate(this);
30354         dd.scroll = false;
30355         this.dd = dd;
30356     }
30357     if(this.modal){
30358         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30359         this.mask.enableDisplayMode("block");
30360         this.mask.hide();
30361         this.el.addClass("x-dlg-modal");
30362     }
30363     if(this.shadow){
30364         this.shadow = new Roo.Shadow({
30365             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30366             offset : this.shadowOffset
30367         });
30368     }else{
30369         this.shadowOffset = 0;
30370     }
30371     if(Roo.useShims && this.shim !== false){
30372         this.shim = this.el.createShim();
30373         this.shim.hide = this.hideAction;
30374         this.shim.hide();
30375     }else{
30376         this.shim = false;
30377     }
30378     if(this.autoTabs){
30379         this.initTabs();
30380     }
30381     if (this.buttons) { 
30382         var bts= this.buttons;
30383         this.buttons = [];
30384         Roo.each(bts, function(b) {
30385             this.addButton(b);
30386         }, this);
30387     }
30388     
30389     
30390     this.addEvents({
30391         /**
30392          * @event keydown
30393          * Fires when a key is pressed
30394          * @param {Roo.BasicDialog} this
30395          * @param {Roo.EventObject} e
30396          */
30397         "keydown" : true,
30398         /**
30399          * @event move
30400          * Fires when this dialog is moved by the user.
30401          * @param {Roo.BasicDialog} this
30402          * @param {Number} x The new page X
30403          * @param {Number} y The new page Y
30404          */
30405         "move" : true,
30406         /**
30407          * @event resize
30408          * Fires when this dialog is resized by the user.
30409          * @param {Roo.BasicDialog} this
30410          * @param {Number} width The new width
30411          * @param {Number} height The new height
30412          */
30413         "resize" : true,
30414         /**
30415          * @event beforehide
30416          * Fires before this dialog is hidden.
30417          * @param {Roo.BasicDialog} this
30418          */
30419         "beforehide" : true,
30420         /**
30421          * @event hide
30422          * Fires when this dialog is hidden.
30423          * @param {Roo.BasicDialog} this
30424          */
30425         "hide" : true,
30426         /**
30427          * @event beforeshow
30428          * Fires before this dialog is shown.
30429          * @param {Roo.BasicDialog} this
30430          */
30431         "beforeshow" : true,
30432         /**
30433          * @event show
30434          * Fires when this dialog is shown.
30435          * @param {Roo.BasicDialog} this
30436          */
30437         "show" : true
30438     });
30439     el.on("keydown", this.onKeyDown, this);
30440     el.on("mousedown", this.toFront, this);
30441     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30442     this.el.hide();
30443     Roo.DialogManager.register(this);
30444     Roo.BasicDialog.superclass.constructor.call(this);
30445 };
30446
30447 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30448     shadowOffset: Roo.isIE ? 6 : 5,
30449     minHeight: 80,
30450     minWidth: 200,
30451     minButtonWidth: 75,
30452     defaultButton: null,
30453     buttonAlign: "right",
30454     tabTag: 'div',
30455     firstShow: true,
30456
30457     /**
30458      * Sets the dialog title text
30459      * @param {String} text The title text to display
30460      * @return {Roo.BasicDialog} this
30461      */
30462     setTitle : function(text){
30463         this.header.update(text);
30464         return this;
30465     },
30466
30467     // private
30468     closeClick : function(){
30469         this.hide();
30470     },
30471
30472     // private
30473     collapseClick : function(){
30474         this[this.collapsed ? "expand" : "collapse"]();
30475     },
30476
30477     /**
30478      * Collapses the dialog to its minimized state (only the title bar is visible).
30479      * Equivalent to the user clicking the collapse dialog button.
30480      */
30481     collapse : function(){
30482         if(!this.collapsed){
30483             this.collapsed = true;
30484             this.el.addClass("x-dlg-collapsed");
30485             this.restoreHeight = this.el.getHeight();
30486             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30487         }
30488     },
30489
30490     /**
30491      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30492      * clicking the expand dialog button.
30493      */
30494     expand : function(){
30495         if(this.collapsed){
30496             this.collapsed = false;
30497             this.el.removeClass("x-dlg-collapsed");
30498             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30499         }
30500     },
30501
30502     /**
30503      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30504      * @return {Roo.TabPanel} The tabs component
30505      */
30506     initTabs : function(){
30507         var tabs = this.getTabs();
30508         while(tabs.getTab(0)){
30509             tabs.removeTab(0);
30510         }
30511         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30512             var dom = el.dom;
30513             tabs.addTab(Roo.id(dom), dom.title);
30514             dom.title = "";
30515         });
30516         tabs.activate(0);
30517         return tabs;
30518     },
30519
30520     // private
30521     beforeResize : function(){
30522         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30523     },
30524
30525     // private
30526     onResize : function(){
30527         this.refreshSize();
30528         this.syncBodyHeight();
30529         this.adjustAssets();
30530         this.focus();
30531         this.fireEvent("resize", this, this.size.width, this.size.height);
30532     },
30533
30534     // private
30535     onKeyDown : function(e){
30536         if(this.isVisible()){
30537             this.fireEvent("keydown", this, e);
30538         }
30539     },
30540
30541     /**
30542      * Resizes the dialog.
30543      * @param {Number} width
30544      * @param {Number} height
30545      * @return {Roo.BasicDialog} this
30546      */
30547     resizeTo : function(width, height){
30548         this.el.setSize(width, height);
30549         this.size = {width: width, height: height};
30550         this.syncBodyHeight();
30551         if(this.fixedcenter){
30552             this.center();
30553         }
30554         if(this.isVisible()){
30555             this.constrainXY();
30556             this.adjustAssets();
30557         }
30558         this.fireEvent("resize", this, width, height);
30559         return this;
30560     },
30561
30562
30563     /**
30564      * Resizes the dialog to fit the specified content size.
30565      * @param {Number} width
30566      * @param {Number} height
30567      * @return {Roo.BasicDialog} this
30568      */
30569     setContentSize : function(w, h){
30570         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30571         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30572         //if(!this.el.isBorderBox()){
30573             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30574             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30575         //}
30576         if(this.tabs){
30577             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30578             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30579         }
30580         this.resizeTo(w, h);
30581         return this;
30582     },
30583
30584     /**
30585      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30586      * executed in response to a particular key being pressed while the dialog is active.
30587      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30588      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30589      * @param {Function} fn The function to call
30590      * @param {Object} scope (optional) The scope of the function
30591      * @return {Roo.BasicDialog} this
30592      */
30593     addKeyListener : function(key, fn, scope){
30594         var keyCode, shift, ctrl, alt;
30595         if(typeof key == "object" && !(key instanceof Array)){
30596             keyCode = key["key"];
30597             shift = key["shift"];
30598             ctrl = key["ctrl"];
30599             alt = key["alt"];
30600         }else{
30601             keyCode = key;
30602         }
30603         var handler = function(dlg, e){
30604             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30605                 var k = e.getKey();
30606                 if(keyCode instanceof Array){
30607                     for(var i = 0, len = keyCode.length; i < len; i++){
30608                         if(keyCode[i] == k){
30609                           fn.call(scope || window, dlg, k, e);
30610                           return;
30611                         }
30612                     }
30613                 }else{
30614                     if(k == keyCode){
30615                         fn.call(scope || window, dlg, k, e);
30616                     }
30617                 }
30618             }
30619         };
30620         this.on("keydown", handler);
30621         return this;
30622     },
30623
30624     /**
30625      * Returns the TabPanel component (creates it if it doesn't exist).
30626      * Note: If you wish to simply check for the existence of tabs without creating them,
30627      * check for a null 'tabs' property.
30628      * @return {Roo.TabPanel} The tabs component
30629      */
30630     getTabs : function(){
30631         if(!this.tabs){
30632             this.el.addClass("x-dlg-auto-tabs");
30633             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30634             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30635         }
30636         return this.tabs;
30637     },
30638
30639     /**
30640      * Adds a button to the footer section of the dialog.
30641      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30642      * object or a valid Roo.DomHelper element config
30643      * @param {Function} handler The function called when the button is clicked
30644      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30645      * @return {Roo.Button} The new button
30646      */
30647     addButton : function(config, handler, scope){
30648         var dh = Roo.DomHelper;
30649         if(!this.footer){
30650             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30651         }
30652         if(!this.btnContainer){
30653             var tb = this.footer.createChild({
30654
30655                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30656                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30657             }, null, true);
30658             this.btnContainer = tb.firstChild.firstChild.firstChild;
30659         }
30660         var bconfig = {
30661             handler: handler,
30662             scope: scope,
30663             minWidth: this.minButtonWidth,
30664             hideParent:true
30665         };
30666         if(typeof config == "string"){
30667             bconfig.text = config;
30668         }else{
30669             if(config.tag){
30670                 bconfig.dhconfig = config;
30671             }else{
30672                 Roo.apply(bconfig, config);
30673             }
30674         }
30675         var fc = false;
30676         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30677             bconfig.position = Math.max(0, bconfig.position);
30678             fc = this.btnContainer.childNodes[bconfig.position];
30679         }
30680          
30681         var btn = new Roo.Button(
30682             fc ? 
30683                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30684                 : this.btnContainer.appendChild(document.createElement("td")),
30685             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30686             bconfig
30687         );
30688         this.syncBodyHeight();
30689         if(!this.buttons){
30690             /**
30691              * Array of all the buttons that have been added to this dialog via addButton
30692              * @type Array
30693              */
30694             this.buttons = [];
30695         }
30696         this.buttons.push(btn);
30697         return btn;
30698     },
30699
30700     /**
30701      * Sets the default button to be focused when the dialog is displayed.
30702      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30703      * @return {Roo.BasicDialog} this
30704      */
30705     setDefaultButton : function(btn){
30706         this.defaultButton = btn;
30707         return this;
30708     },
30709
30710     // private
30711     getHeaderFooterHeight : function(safe){
30712         var height = 0;
30713         if(this.header){
30714            height += this.header.getHeight();
30715         }
30716         if(this.footer){
30717            var fm = this.footer.getMargins();
30718             height += (this.footer.getHeight()+fm.top+fm.bottom);
30719         }
30720         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30721         height += this.centerBg.getPadding("tb");
30722         return height;
30723     },
30724
30725     // private
30726     syncBodyHeight : function()
30727     {
30728         var bd = this.body, // the text
30729             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30730             bw = this.bwrap;
30731         var height = this.size.height - this.getHeaderFooterHeight(false);
30732         bd.setHeight(height-bd.getMargins("tb"));
30733         var hh = this.header.getHeight();
30734         var h = this.size.height-hh;
30735         cb.setHeight(h);
30736         
30737         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30738         bw.setHeight(h-cb.getPadding("tb"));
30739         
30740         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30741         bd.setWidth(bw.getWidth(true));
30742         if(this.tabs){
30743             this.tabs.syncHeight();
30744             if(Roo.isIE){
30745                 this.tabs.el.repaint();
30746             }
30747         }
30748     },
30749
30750     /**
30751      * Restores the previous state of the dialog if Roo.state is configured.
30752      * @return {Roo.BasicDialog} this
30753      */
30754     restoreState : function(){
30755         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30756         if(box && box.width){
30757             this.xy = [box.x, box.y];
30758             this.resizeTo(box.width, box.height);
30759         }
30760         return this;
30761     },
30762
30763     // private
30764     beforeShow : function(){
30765         this.expand();
30766         if(this.fixedcenter){
30767             this.xy = this.el.getCenterXY(true);
30768         }
30769         if(this.modal){
30770             Roo.get(document.body).addClass("x-body-masked");
30771             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30772             this.mask.show();
30773         }
30774         this.constrainXY();
30775     },
30776
30777     // private
30778     animShow : function(){
30779         var b = Roo.get(this.animateTarget).getBox();
30780         this.proxy.setSize(b.width, b.height);
30781         this.proxy.setLocation(b.x, b.y);
30782         this.proxy.show();
30783         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30784                     true, .35, this.showEl.createDelegate(this));
30785     },
30786
30787     /**
30788      * Shows the dialog.
30789      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30790      * @return {Roo.BasicDialog} this
30791      */
30792     show : function(animateTarget){
30793         if (this.fireEvent("beforeshow", this) === false){
30794             return;
30795         }
30796         if(this.syncHeightBeforeShow){
30797             this.syncBodyHeight();
30798         }else if(this.firstShow){
30799             this.firstShow = false;
30800             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30801         }
30802         this.animateTarget = animateTarget || this.animateTarget;
30803         if(!this.el.isVisible()){
30804             this.beforeShow();
30805             if(this.animateTarget && Roo.get(this.animateTarget)){
30806                 this.animShow();
30807             }else{
30808                 this.showEl();
30809             }
30810         }
30811         return this;
30812     },
30813
30814     // private
30815     showEl : function(){
30816         this.proxy.hide();
30817         this.el.setXY(this.xy);
30818         this.el.show();
30819         this.adjustAssets(true);
30820         this.toFront();
30821         this.focus();
30822         // IE peekaboo bug - fix found by Dave Fenwick
30823         if(Roo.isIE){
30824             this.el.repaint();
30825         }
30826         this.fireEvent("show", this);
30827     },
30828
30829     /**
30830      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30831      * dialog itself will receive focus.
30832      */
30833     focus : function(){
30834         if(this.defaultButton){
30835             this.defaultButton.focus();
30836         }else{
30837             this.focusEl.focus();
30838         }
30839     },
30840
30841     // private
30842     constrainXY : function(){
30843         if(this.constraintoviewport !== false){
30844             if(!this.viewSize){
30845                 if(this.container){
30846                     var s = this.container.getSize();
30847                     this.viewSize = [s.width, s.height];
30848                 }else{
30849                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30850                 }
30851             }
30852             var s = Roo.get(this.container||document).getScroll();
30853
30854             var x = this.xy[0], y = this.xy[1];
30855             var w = this.size.width, h = this.size.height;
30856             var vw = this.viewSize[0], vh = this.viewSize[1];
30857             // only move it if it needs it
30858             var moved = false;
30859             // first validate right/bottom
30860             if(x + w > vw+s.left){
30861                 x = vw - w;
30862                 moved = true;
30863             }
30864             if(y + h > vh+s.top){
30865                 y = vh - h;
30866                 moved = true;
30867             }
30868             // then make sure top/left isn't negative
30869             if(x < s.left){
30870                 x = s.left;
30871                 moved = true;
30872             }
30873             if(y < s.top){
30874                 y = s.top;
30875                 moved = true;
30876             }
30877             if(moved){
30878                 // cache xy
30879                 this.xy = [x, y];
30880                 if(this.isVisible()){
30881                     this.el.setLocation(x, y);
30882                     this.adjustAssets();
30883                 }
30884             }
30885         }
30886     },
30887
30888     // private
30889     onDrag : function(){
30890         if(!this.proxyDrag){
30891             this.xy = this.el.getXY();
30892             this.adjustAssets();
30893         }
30894     },
30895
30896     // private
30897     adjustAssets : function(doShow){
30898         var x = this.xy[0], y = this.xy[1];
30899         var w = this.size.width, h = this.size.height;
30900         if(doShow === true){
30901             if(this.shadow){
30902                 this.shadow.show(this.el);
30903             }
30904             if(this.shim){
30905                 this.shim.show();
30906             }
30907         }
30908         if(this.shadow && this.shadow.isVisible()){
30909             this.shadow.show(this.el);
30910         }
30911         if(this.shim && this.shim.isVisible()){
30912             this.shim.setBounds(x, y, w, h);
30913         }
30914     },
30915
30916     // private
30917     adjustViewport : function(w, h){
30918         if(!w || !h){
30919             w = Roo.lib.Dom.getViewWidth();
30920             h = Roo.lib.Dom.getViewHeight();
30921         }
30922         // cache the size
30923         this.viewSize = [w, h];
30924         if(this.modal && this.mask.isVisible()){
30925             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30926             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30927         }
30928         if(this.isVisible()){
30929             this.constrainXY();
30930         }
30931     },
30932
30933     /**
30934      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30935      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30936      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30937      */
30938     destroy : function(removeEl){
30939         if(this.isVisible()){
30940             this.animateTarget = null;
30941             this.hide();
30942         }
30943         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30944         if(this.tabs){
30945             this.tabs.destroy(removeEl);
30946         }
30947         Roo.destroy(
30948              this.shim,
30949              this.proxy,
30950              this.resizer,
30951              this.close,
30952              this.mask
30953         );
30954         if(this.dd){
30955             this.dd.unreg();
30956         }
30957         if(this.buttons){
30958            for(var i = 0, len = this.buttons.length; i < len; i++){
30959                this.buttons[i].destroy();
30960            }
30961         }
30962         this.el.removeAllListeners();
30963         if(removeEl === true){
30964             this.el.update("");
30965             this.el.remove();
30966         }
30967         Roo.DialogManager.unregister(this);
30968     },
30969
30970     // private
30971     startMove : function(){
30972         if(this.proxyDrag){
30973             this.proxy.show();
30974         }
30975         if(this.constraintoviewport !== false){
30976             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30977         }
30978     },
30979
30980     // private
30981     endMove : function(){
30982         if(!this.proxyDrag){
30983             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30984         }else{
30985             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30986             this.proxy.hide();
30987         }
30988         this.refreshSize();
30989         this.adjustAssets();
30990         this.focus();
30991         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30992     },
30993
30994     /**
30995      * Brings this dialog to the front of any other visible dialogs
30996      * @return {Roo.BasicDialog} this
30997      */
30998     toFront : function(){
30999         Roo.DialogManager.bringToFront(this);
31000         return this;
31001     },
31002
31003     /**
31004      * Sends this dialog to the back (under) of any other visible dialogs
31005      * @return {Roo.BasicDialog} this
31006      */
31007     toBack : function(){
31008         Roo.DialogManager.sendToBack(this);
31009         return this;
31010     },
31011
31012     /**
31013      * Centers this dialog in the viewport
31014      * @return {Roo.BasicDialog} this
31015      */
31016     center : function(){
31017         var xy = this.el.getCenterXY(true);
31018         this.moveTo(xy[0], xy[1]);
31019         return this;
31020     },
31021
31022     /**
31023      * Moves the dialog's top-left corner to the specified point
31024      * @param {Number} x
31025      * @param {Number} y
31026      * @return {Roo.BasicDialog} this
31027      */
31028     moveTo : function(x, y){
31029         this.xy = [x,y];
31030         if(this.isVisible()){
31031             this.el.setXY(this.xy);
31032             this.adjustAssets();
31033         }
31034         return this;
31035     },
31036
31037     /**
31038      * Aligns the dialog to the specified element
31039      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31040      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31041      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31042      * @return {Roo.BasicDialog} this
31043      */
31044     alignTo : function(element, position, offsets){
31045         this.xy = this.el.getAlignToXY(element, position, offsets);
31046         if(this.isVisible()){
31047             this.el.setXY(this.xy);
31048             this.adjustAssets();
31049         }
31050         return this;
31051     },
31052
31053     /**
31054      * Anchors an element to another element and realigns it when the window is resized.
31055      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31056      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31057      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31058      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31059      * is a number, it is used as the buffer delay (defaults to 50ms).
31060      * @return {Roo.BasicDialog} this
31061      */
31062     anchorTo : function(el, alignment, offsets, monitorScroll){
31063         var action = function(){
31064             this.alignTo(el, alignment, offsets);
31065         };
31066         Roo.EventManager.onWindowResize(action, this);
31067         var tm = typeof monitorScroll;
31068         if(tm != 'undefined'){
31069             Roo.EventManager.on(window, 'scroll', action, this,
31070                 {buffer: tm == 'number' ? monitorScroll : 50});
31071         }
31072         action.call(this);
31073         return this;
31074     },
31075
31076     /**
31077      * Returns true if the dialog is visible
31078      * @return {Boolean}
31079      */
31080     isVisible : function(){
31081         return this.el.isVisible();
31082     },
31083
31084     // private
31085     animHide : function(callback){
31086         var b = Roo.get(this.animateTarget).getBox();
31087         this.proxy.show();
31088         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31089         this.el.hide();
31090         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31091                     this.hideEl.createDelegate(this, [callback]));
31092     },
31093
31094     /**
31095      * Hides the dialog.
31096      * @param {Function} callback (optional) Function to call when the dialog is hidden
31097      * @return {Roo.BasicDialog} this
31098      */
31099     hide : function(callback){
31100         if (this.fireEvent("beforehide", this) === false){
31101             return;
31102         }
31103         if(this.shadow){
31104             this.shadow.hide();
31105         }
31106         if(this.shim) {
31107           this.shim.hide();
31108         }
31109         // sometimes animateTarget seems to get set.. causing problems...
31110         // this just double checks..
31111         if(this.animateTarget && Roo.get(this.animateTarget)) {
31112            this.animHide(callback);
31113         }else{
31114             this.el.hide();
31115             this.hideEl(callback);
31116         }
31117         return this;
31118     },
31119
31120     // private
31121     hideEl : function(callback){
31122         this.proxy.hide();
31123         if(this.modal){
31124             this.mask.hide();
31125             Roo.get(document.body).removeClass("x-body-masked");
31126         }
31127         this.fireEvent("hide", this);
31128         if(typeof callback == "function"){
31129             callback();
31130         }
31131     },
31132
31133     // private
31134     hideAction : function(){
31135         this.setLeft("-10000px");
31136         this.setTop("-10000px");
31137         this.setStyle("visibility", "hidden");
31138     },
31139
31140     // private
31141     refreshSize : function(){
31142         this.size = this.el.getSize();
31143         this.xy = this.el.getXY();
31144         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31145     },
31146
31147     // private
31148     // z-index is managed by the DialogManager and may be overwritten at any time
31149     setZIndex : function(index){
31150         if(this.modal){
31151             this.mask.setStyle("z-index", index);
31152         }
31153         if(this.shim){
31154             this.shim.setStyle("z-index", ++index);
31155         }
31156         if(this.shadow){
31157             this.shadow.setZIndex(++index);
31158         }
31159         this.el.setStyle("z-index", ++index);
31160         if(this.proxy){
31161             this.proxy.setStyle("z-index", ++index);
31162         }
31163         if(this.resizer){
31164             this.resizer.proxy.setStyle("z-index", ++index);
31165         }
31166
31167         this.lastZIndex = index;
31168     },
31169
31170     /**
31171      * Returns the element for this dialog
31172      * @return {Roo.Element} The underlying dialog Element
31173      */
31174     getEl : function(){
31175         return this.el;
31176     }
31177 });
31178
31179 /**
31180  * @class Roo.DialogManager
31181  * Provides global access to BasicDialogs that have been created and
31182  * support for z-indexing (layering) multiple open dialogs.
31183  */
31184 Roo.DialogManager = function(){
31185     var list = {};
31186     var accessList = [];
31187     var front = null;
31188
31189     // private
31190     var sortDialogs = function(d1, d2){
31191         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31192     };
31193
31194     // private
31195     var orderDialogs = function(){
31196         accessList.sort(sortDialogs);
31197         var seed = Roo.DialogManager.zseed;
31198         for(var i = 0, len = accessList.length; i < len; i++){
31199             var dlg = accessList[i];
31200             if(dlg){
31201                 dlg.setZIndex(seed + (i*10));
31202             }
31203         }
31204     };
31205
31206     return {
31207         /**
31208          * The starting z-index for BasicDialogs (defaults to 9000)
31209          * @type Number The z-index value
31210          */
31211         zseed : 9000,
31212
31213         // private
31214         register : function(dlg){
31215             list[dlg.id] = dlg;
31216             accessList.push(dlg);
31217         },
31218
31219         // private
31220         unregister : function(dlg){
31221             delete list[dlg.id];
31222             var i=0;
31223             var len=0;
31224             if(!accessList.indexOf){
31225                 for(  i = 0, len = accessList.length; i < len; i++){
31226                     if(accessList[i] == dlg){
31227                         accessList.splice(i, 1);
31228                         return;
31229                     }
31230                 }
31231             }else{
31232                  i = accessList.indexOf(dlg);
31233                 if(i != -1){
31234                     accessList.splice(i, 1);
31235                 }
31236             }
31237         },
31238
31239         /**
31240          * Gets a registered dialog by id
31241          * @param {String/Object} id The id of the dialog or a dialog
31242          * @return {Roo.BasicDialog} this
31243          */
31244         get : function(id){
31245             return typeof id == "object" ? id : list[id];
31246         },
31247
31248         /**
31249          * Brings the specified dialog to the front
31250          * @param {String/Object} dlg The id of the dialog or a dialog
31251          * @return {Roo.BasicDialog} this
31252          */
31253         bringToFront : function(dlg){
31254             dlg = this.get(dlg);
31255             if(dlg != front){
31256                 front = dlg;
31257                 dlg._lastAccess = new Date().getTime();
31258                 orderDialogs();
31259             }
31260             return dlg;
31261         },
31262
31263         /**
31264          * Sends the specified dialog to the back
31265          * @param {String/Object} dlg The id of the dialog or a dialog
31266          * @return {Roo.BasicDialog} this
31267          */
31268         sendToBack : function(dlg){
31269             dlg = this.get(dlg);
31270             dlg._lastAccess = -(new Date().getTime());
31271             orderDialogs();
31272             return dlg;
31273         },
31274
31275         /**
31276          * Hides all dialogs
31277          */
31278         hideAll : function(){
31279             for(var id in list){
31280                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31281                     list[id].hide();
31282                 }
31283             }
31284         }
31285     };
31286 }();
31287
31288 /**
31289  * @class Roo.LayoutDialog
31290  * @extends Roo.BasicDialog
31291  * Dialog which provides adjustments for working with a layout in a Dialog.
31292  * Add your necessary layout config options to the dialog's config.<br>
31293  * Example usage (including a nested layout):
31294  * <pre><code>
31295 if(!dialog){
31296     dialog = new Roo.LayoutDialog("download-dlg", {
31297         modal: true,
31298         width:600,
31299         height:450,
31300         shadow:true,
31301         minWidth:500,
31302         minHeight:350,
31303         autoTabs:true,
31304         proxyDrag:true,
31305         // layout config merges with the dialog config
31306         center:{
31307             tabPosition: "top",
31308             alwaysShowTabs: true
31309         }
31310     });
31311     dialog.addKeyListener(27, dialog.hide, dialog);
31312     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31313     dialog.addButton("Build It!", this.getDownload, this);
31314
31315     // we can even add nested layouts
31316     var innerLayout = new Roo.BorderLayout("dl-inner", {
31317         east: {
31318             initialSize: 200,
31319             autoScroll:true,
31320             split:true
31321         },
31322         center: {
31323             autoScroll:true
31324         }
31325     });
31326     innerLayout.beginUpdate();
31327     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31328     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31329     innerLayout.endUpdate(true);
31330
31331     var layout = dialog.getLayout();
31332     layout.beginUpdate();
31333     layout.add("center", new Roo.ContentPanel("standard-panel",
31334                         {title: "Download the Source", fitToFrame:true}));
31335     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31336                {title: "Build your own roo.js"}));
31337     layout.getRegion("center").showPanel(sp);
31338     layout.endUpdate();
31339 }
31340 </code></pre>
31341     * @constructor
31342     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31343     * @param {Object} config configuration options
31344   */
31345 Roo.LayoutDialog = function(el, cfg){
31346     
31347     var config=  cfg;
31348     if (typeof(cfg) == 'undefined') {
31349         config = Roo.apply({}, el);
31350         // not sure why we use documentElement here.. - it should always be body.
31351         // IE7 borks horribly if we use documentElement.
31352         // webkit also does not like documentElement - it creates a body element...
31353         el = Roo.get( document.body || document.documentElement ).createChild();
31354         //config.autoCreate = true;
31355     }
31356     
31357     
31358     config.autoTabs = false;
31359     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31360     this.body.setStyle({overflow:"hidden", position:"relative"});
31361     this.layout = new Roo.BorderLayout(this.body.dom, config);
31362     this.layout.monitorWindowResize = false;
31363     this.el.addClass("x-dlg-auto-layout");
31364     // fix case when center region overwrites center function
31365     this.center = Roo.BasicDialog.prototype.center;
31366     this.on("show", this.layout.layout, this.layout, true);
31367     if (config.items) {
31368         var xitems = config.items;
31369         delete config.items;
31370         Roo.each(xitems, this.addxtype, this);
31371     }
31372     
31373     
31374 };
31375 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31376     /**
31377      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31378      * @deprecated
31379      */
31380     endUpdate : function(){
31381         this.layout.endUpdate();
31382     },
31383
31384     /**
31385      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31386      *  @deprecated
31387      */
31388     beginUpdate : function(){
31389         this.layout.beginUpdate();
31390     },
31391
31392     /**
31393      * Get the BorderLayout for this dialog
31394      * @return {Roo.BorderLayout}
31395      */
31396     getLayout : function(){
31397         return this.layout;
31398     },
31399
31400     showEl : function(){
31401         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31402         if(Roo.isIE7){
31403             this.layout.layout();
31404         }
31405     },
31406
31407     // private
31408     // Use the syncHeightBeforeShow config option to control this automatically
31409     syncBodyHeight : function(){
31410         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31411         if(this.layout){this.layout.layout();}
31412     },
31413     
31414       /**
31415      * Add an xtype element (actually adds to the layout.)
31416      * @return {Object} xdata xtype object data.
31417      */
31418     
31419     addxtype : function(c) {
31420         return this.layout.addxtype(c);
31421     }
31422 });/*
31423  * Based on:
31424  * Ext JS Library 1.1.1
31425  * Copyright(c) 2006-2007, Ext JS, LLC.
31426  *
31427  * Originally Released Under LGPL - original licence link has changed is not relivant.
31428  *
31429  * Fork - LGPL
31430  * <script type="text/javascript">
31431  */
31432  
31433 /**
31434  * @class Roo.MessageBox
31435  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31436  * Example usage:
31437  *<pre><code>
31438 // Basic alert:
31439 Roo.Msg.alert('Status', 'Changes saved successfully.');
31440
31441 // Prompt for user data:
31442 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31443     if (btn == 'ok'){
31444         // process text value...
31445     }
31446 });
31447
31448 // Show a dialog using config options:
31449 Roo.Msg.show({
31450    title:'Save Changes?',
31451    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31452    buttons: Roo.Msg.YESNOCANCEL,
31453    fn: processResult,
31454    animEl: 'elId'
31455 });
31456 </code></pre>
31457  * @singleton
31458  */
31459 Roo.MessageBox = function(){
31460     var dlg, opt, mask, waitTimer;
31461     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31462     var buttons, activeTextEl, bwidth;
31463
31464     // private
31465     var handleButton = function(button){
31466         dlg.hide();
31467         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31468     };
31469
31470     // private
31471     var handleHide = function(){
31472         if(opt && opt.cls){
31473             dlg.el.removeClass(opt.cls);
31474         }
31475         if(waitTimer){
31476             Roo.TaskMgr.stop(waitTimer);
31477             waitTimer = null;
31478         }
31479     };
31480
31481     // private
31482     var updateButtons = function(b){
31483         var width = 0;
31484         if(!b){
31485             buttons["ok"].hide();
31486             buttons["cancel"].hide();
31487             buttons["yes"].hide();
31488             buttons["no"].hide();
31489             dlg.footer.dom.style.display = 'none';
31490             return width;
31491         }
31492         dlg.footer.dom.style.display = '';
31493         for(var k in buttons){
31494             if(typeof buttons[k] != "function"){
31495                 if(b[k]){
31496                     buttons[k].show();
31497                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31498                     width += buttons[k].el.getWidth()+15;
31499                 }else{
31500                     buttons[k].hide();
31501                 }
31502             }
31503         }
31504         return width;
31505     };
31506
31507     // private
31508     var handleEsc = function(d, k, e){
31509         if(opt && opt.closable !== false){
31510             dlg.hide();
31511         }
31512         if(e){
31513             e.stopEvent();
31514         }
31515     };
31516
31517     return {
31518         /**
31519          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31520          * @return {Roo.BasicDialog} The BasicDialog element
31521          */
31522         getDialog : function(){
31523            if(!dlg){
31524                 dlg = new Roo.BasicDialog("x-msg-box", {
31525                     autoCreate : true,
31526                     shadow: true,
31527                     draggable: true,
31528                     resizable:false,
31529                     constraintoviewport:false,
31530                     fixedcenter:true,
31531                     collapsible : false,
31532                     shim:true,
31533                     modal: true,
31534                     width:400, height:100,
31535                     buttonAlign:"center",
31536                     closeClick : function(){
31537                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31538                             handleButton("no");
31539                         }else{
31540                             handleButton("cancel");
31541                         }
31542                     }
31543                 });
31544                 dlg.on("hide", handleHide);
31545                 mask = dlg.mask;
31546                 dlg.addKeyListener(27, handleEsc);
31547                 buttons = {};
31548                 var bt = this.buttonText;
31549                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31550                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31551                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31552                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31553                 bodyEl = dlg.body.createChild({
31554
31555                     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>'
31556                 });
31557                 msgEl = bodyEl.dom.firstChild;
31558                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31559                 textboxEl.enableDisplayMode();
31560                 textboxEl.addKeyListener([10,13], function(){
31561                     if(dlg.isVisible() && opt && opt.buttons){
31562                         if(opt.buttons.ok){
31563                             handleButton("ok");
31564                         }else if(opt.buttons.yes){
31565                             handleButton("yes");
31566                         }
31567                     }
31568                 });
31569                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31570                 textareaEl.enableDisplayMode();
31571                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31572                 progressEl.enableDisplayMode();
31573                 var pf = progressEl.dom.firstChild;
31574                 if (pf) {
31575                     pp = Roo.get(pf.firstChild);
31576                     pp.setHeight(pf.offsetHeight);
31577                 }
31578                 
31579             }
31580             return dlg;
31581         },
31582
31583         /**
31584          * Updates the message box body text
31585          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31586          * the XHTML-compliant non-breaking space character '&amp;#160;')
31587          * @return {Roo.MessageBox} This message box
31588          */
31589         updateText : function(text){
31590             if(!dlg.isVisible() && !opt.width){
31591                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31592             }
31593             msgEl.innerHTML = text || '&#160;';
31594       
31595             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31596             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31597             var w = Math.max(
31598                     Math.min(opt.width || cw , this.maxWidth), 
31599                     Math.max(opt.minWidth || this.minWidth, bwidth)
31600             );
31601             if(opt.prompt){
31602                 activeTextEl.setWidth(w);
31603             }
31604             if(dlg.isVisible()){
31605                 dlg.fixedcenter = false;
31606             }
31607             // to big, make it scroll. = But as usual stupid IE does not support
31608             // !important..
31609             
31610             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31611                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31612                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31613             } else {
31614                 bodyEl.dom.style.height = '';
31615                 bodyEl.dom.style.overflowY = '';
31616             }
31617             if (cw > w) {
31618                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31619             } else {
31620                 bodyEl.dom.style.overflowX = '';
31621             }
31622             
31623             dlg.setContentSize(w, bodyEl.getHeight());
31624             if(dlg.isVisible()){
31625                 dlg.fixedcenter = true;
31626             }
31627             return this;
31628         },
31629
31630         /**
31631          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31632          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31633          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31634          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31635          * @return {Roo.MessageBox} This message box
31636          */
31637         updateProgress : function(value, text){
31638             if(text){
31639                 this.updateText(text);
31640             }
31641             if (pp) { // weird bug on my firefox - for some reason this is not defined
31642                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31643             }
31644             return this;
31645         },        
31646
31647         /**
31648          * Returns true if the message box is currently displayed
31649          * @return {Boolean} True if the message box is visible, else false
31650          */
31651         isVisible : function(){
31652             return dlg && dlg.isVisible();  
31653         },
31654
31655         /**
31656          * Hides the message box if it is displayed
31657          */
31658         hide : function(){
31659             if(this.isVisible()){
31660                 dlg.hide();
31661             }  
31662         },
31663
31664         /**
31665          * Displays a new message box, or reinitializes an existing message box, based on the config options
31666          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31667          * The following config object properties are supported:
31668          * <pre>
31669 Property    Type             Description
31670 ----------  ---------------  ------------------------------------------------------------------------------------
31671 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31672                                    closes (defaults to undefined)
31673 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31674                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31675 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31676                                    progress and wait dialogs will ignore this property and always hide the
31677                                    close button as they can only be closed programmatically.
31678 cls               String           A custom CSS class to apply to the message box element
31679 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31680                                    displayed (defaults to 75)
31681 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31682                                    function will be btn (the name of the button that was clicked, if applicable,
31683                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31684                                    Progress and wait dialogs will ignore this option since they do not respond to
31685                                    user actions and can only be closed programmatically, so any required function
31686                                    should be called by the same code after it closes the dialog.
31687 icon              String           A CSS class that provides a background image to be used as an icon for
31688                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31689 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31690 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31691 modal             Boolean          False to allow user interaction with the page while the message box is
31692                                    displayed (defaults to true)
31693 msg               String           A string that will replace the existing message box body text (defaults
31694                                    to the XHTML-compliant non-breaking space character '&#160;')
31695 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31696 progress          Boolean          True to display a progress bar (defaults to false)
31697 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31698 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31699 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31700 title             String           The title text
31701 value             String           The string value to set into the active textbox element if displayed
31702 wait              Boolean          True to display a progress bar (defaults to false)
31703 width             Number           The width of the dialog in pixels
31704 </pre>
31705          *
31706          * Example usage:
31707          * <pre><code>
31708 Roo.Msg.show({
31709    title: 'Address',
31710    msg: 'Please enter your address:',
31711    width: 300,
31712    buttons: Roo.MessageBox.OKCANCEL,
31713    multiline: true,
31714    fn: saveAddress,
31715    animEl: 'addAddressBtn'
31716 });
31717 </code></pre>
31718          * @param {Object} config Configuration options
31719          * @return {Roo.MessageBox} This message box
31720          */
31721         show : function(options)
31722         {
31723             
31724             // this causes nightmares if you show one dialog after another
31725             // especially on callbacks..
31726              
31727             if(this.isVisible()){
31728                 
31729                 this.hide();
31730                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31731                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31732                 Roo.log("New Dialog Message:" +  options.msg )
31733                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31734                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31735                 
31736             }
31737             var d = this.getDialog();
31738             opt = options;
31739             d.setTitle(opt.title || "&#160;");
31740             d.close.setDisplayed(opt.closable !== false);
31741             activeTextEl = textboxEl;
31742             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31743             if(opt.prompt){
31744                 if(opt.multiline){
31745                     textboxEl.hide();
31746                     textareaEl.show();
31747                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31748                         opt.multiline : this.defaultTextHeight);
31749                     activeTextEl = textareaEl;
31750                 }else{
31751                     textboxEl.show();
31752                     textareaEl.hide();
31753                 }
31754             }else{
31755                 textboxEl.hide();
31756                 textareaEl.hide();
31757             }
31758             progressEl.setDisplayed(opt.progress === true);
31759             this.updateProgress(0);
31760             activeTextEl.dom.value = opt.value || "";
31761             if(opt.prompt){
31762                 dlg.setDefaultButton(activeTextEl);
31763             }else{
31764                 var bs = opt.buttons;
31765                 var db = null;
31766                 if(bs && bs.ok){
31767                     db = buttons["ok"];
31768                 }else if(bs && bs.yes){
31769                     db = buttons["yes"];
31770                 }
31771                 dlg.setDefaultButton(db);
31772             }
31773             bwidth = updateButtons(opt.buttons);
31774             this.updateText(opt.msg);
31775             if(opt.cls){
31776                 d.el.addClass(opt.cls);
31777             }
31778             d.proxyDrag = opt.proxyDrag === true;
31779             d.modal = opt.modal !== false;
31780             d.mask = opt.modal !== false ? mask : false;
31781             if(!d.isVisible()){
31782                 // force it to the end of the z-index stack so it gets a cursor in FF
31783                 document.body.appendChild(dlg.el.dom);
31784                 d.animateTarget = null;
31785                 d.show(options.animEl);
31786             }
31787             return this;
31788         },
31789
31790         /**
31791          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31792          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31793          * and closing the message box when the process is complete.
31794          * @param {String} title The title bar text
31795          * @param {String} msg The message box body text
31796          * @return {Roo.MessageBox} This message box
31797          */
31798         progress : function(title, msg){
31799             this.show({
31800                 title : title,
31801                 msg : msg,
31802                 buttons: false,
31803                 progress:true,
31804                 closable:false,
31805                 minWidth: this.minProgressWidth,
31806                 modal : true
31807             });
31808             return this;
31809         },
31810
31811         /**
31812          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31813          * If a callback function is passed it will be called after the user clicks the button, and the
31814          * id of the button that was clicked will be passed as the only parameter to the callback
31815          * (could also be the top-right close button).
31816          * @param {String} title The title bar text
31817          * @param {String} msg The message box body text
31818          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31819          * @param {Object} scope (optional) The scope of the callback function
31820          * @return {Roo.MessageBox} This message box
31821          */
31822         alert : function(title, msg, fn, scope){
31823             this.show({
31824                 title : title,
31825                 msg : msg,
31826                 buttons: this.OK,
31827                 fn: fn,
31828                 scope : scope,
31829                 modal : true
31830             });
31831             return this;
31832         },
31833
31834         /**
31835          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31836          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31837          * You are responsible for closing the message box when the process is complete.
31838          * @param {String} msg The message box body text
31839          * @param {String} title (optional) The title bar text
31840          * @return {Roo.MessageBox} This message box
31841          */
31842         wait : function(msg, title){
31843             this.show({
31844                 title : title,
31845                 msg : msg,
31846                 buttons: false,
31847                 closable:false,
31848                 progress:true,
31849                 modal:true,
31850                 width:300,
31851                 wait:true
31852             });
31853             waitTimer = Roo.TaskMgr.start({
31854                 run: function(i){
31855                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31856                 },
31857                 interval: 1000
31858             });
31859             return this;
31860         },
31861
31862         /**
31863          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31864          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31865          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31866          * @param {String} title The title bar text
31867          * @param {String} msg The message box body text
31868          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31869          * @param {Object} scope (optional) The scope of the callback function
31870          * @return {Roo.MessageBox} This message box
31871          */
31872         confirm : function(title, msg, fn, scope){
31873             this.show({
31874                 title : title,
31875                 msg : msg,
31876                 buttons: this.YESNO,
31877                 fn: fn,
31878                 scope : scope,
31879                 modal : true
31880             });
31881             return this;
31882         },
31883
31884         /**
31885          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31886          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31887          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31888          * (could also be the top-right close button) and the text that was entered will be passed as the two
31889          * parameters to the callback.
31890          * @param {String} title The title bar text
31891          * @param {String} msg The message box body text
31892          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31893          * @param {Object} scope (optional) The scope of the callback function
31894          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31895          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31896          * @return {Roo.MessageBox} This message box
31897          */
31898         prompt : function(title, msg, fn, scope, multiline){
31899             this.show({
31900                 title : title,
31901                 msg : msg,
31902                 buttons: this.OKCANCEL,
31903                 fn: fn,
31904                 minWidth:250,
31905                 scope : scope,
31906                 prompt:true,
31907                 multiline: multiline,
31908                 modal : true
31909             });
31910             return this;
31911         },
31912
31913         /**
31914          * Button config that displays a single OK button
31915          * @type Object
31916          */
31917         OK : {ok:true},
31918         /**
31919          * Button config that displays Yes and No buttons
31920          * @type Object
31921          */
31922         YESNO : {yes:true, no:true},
31923         /**
31924          * Button config that displays OK and Cancel buttons
31925          * @type Object
31926          */
31927         OKCANCEL : {ok:true, cancel:true},
31928         /**
31929          * Button config that displays Yes, No and Cancel buttons
31930          * @type Object
31931          */
31932         YESNOCANCEL : {yes:true, no:true, cancel:true},
31933
31934         /**
31935          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31936          * @type Number
31937          */
31938         defaultTextHeight : 75,
31939         /**
31940          * The maximum width in pixels of the message box (defaults to 600)
31941          * @type Number
31942          */
31943         maxWidth : 600,
31944         /**
31945          * The minimum width in pixels of the message box (defaults to 100)
31946          * @type Number
31947          */
31948         minWidth : 100,
31949         /**
31950          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31951          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31952          * @type Number
31953          */
31954         minProgressWidth : 250,
31955         /**
31956          * An object containing the default button text strings that can be overriden for localized language support.
31957          * Supported properties are: ok, cancel, yes and no.
31958          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31959          * @type Object
31960          */
31961         buttonText : {
31962             ok : "OK",
31963             cancel : "Cancel",
31964             yes : "Yes",
31965             no : "No"
31966         }
31967     };
31968 }();
31969
31970 /**
31971  * Shorthand for {@link Roo.MessageBox}
31972  */
31973 Roo.Msg = Roo.MessageBox;/*
31974  * Based on:
31975  * Ext JS Library 1.1.1
31976  * Copyright(c) 2006-2007, Ext JS, LLC.
31977  *
31978  * Originally Released Under LGPL - original licence link has changed is not relivant.
31979  *
31980  * Fork - LGPL
31981  * <script type="text/javascript">
31982  */
31983 /**
31984  * @class Roo.QuickTips
31985  * Provides attractive and customizable tooltips for any element.
31986  * @singleton
31987  */
31988 Roo.QuickTips = function(){
31989     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31990     var ce, bd, xy, dd;
31991     var visible = false, disabled = true, inited = false;
31992     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31993     
31994     var onOver = function(e){
31995         if(disabled){
31996             return;
31997         }
31998         var t = e.getTarget();
31999         if(!t || t.nodeType !== 1 || t == document || t == document.body){
32000             return;
32001         }
32002         if(ce && t == ce.el){
32003             clearTimeout(hideProc);
32004             return;
32005         }
32006         if(t && tagEls[t.id]){
32007             tagEls[t.id].el = t;
32008             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32009             return;
32010         }
32011         var ttp, et = Roo.fly(t);
32012         var ns = cfg.namespace;
32013         if(tm.interceptTitles && t.title){
32014             ttp = t.title;
32015             t.qtip = ttp;
32016             t.removeAttribute("title");
32017             e.preventDefault();
32018         }else{
32019             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32020         }
32021         if(ttp){
32022             showProc = show.defer(tm.showDelay, tm, [{
32023                 el: t, 
32024                 text: ttp, 
32025                 width: et.getAttributeNS(ns, cfg.width),
32026                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32027                 title: et.getAttributeNS(ns, cfg.title),
32028                     cls: et.getAttributeNS(ns, cfg.cls)
32029             }]);
32030         }
32031     };
32032     
32033     var onOut = function(e){
32034         clearTimeout(showProc);
32035         var t = e.getTarget();
32036         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32037             hideProc = setTimeout(hide, tm.hideDelay);
32038         }
32039     };
32040     
32041     var onMove = function(e){
32042         if(disabled){
32043             return;
32044         }
32045         xy = e.getXY();
32046         xy[1] += 18;
32047         if(tm.trackMouse && ce){
32048             el.setXY(xy);
32049         }
32050     };
32051     
32052     var onDown = function(e){
32053         clearTimeout(showProc);
32054         clearTimeout(hideProc);
32055         if(!e.within(el)){
32056             if(tm.hideOnClick){
32057                 hide();
32058                 tm.disable();
32059                 tm.enable.defer(100, tm);
32060             }
32061         }
32062     };
32063     
32064     var getPad = function(){
32065         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32066     };
32067
32068     var show = function(o){
32069         if(disabled){
32070             return;
32071         }
32072         clearTimeout(dismissProc);
32073         ce = o;
32074         if(removeCls){ // in case manually hidden
32075             el.removeClass(removeCls);
32076             removeCls = null;
32077         }
32078         if(ce.cls){
32079             el.addClass(ce.cls);
32080             removeCls = ce.cls;
32081         }
32082         if(ce.title){
32083             tipTitle.update(ce.title);
32084             tipTitle.show();
32085         }else{
32086             tipTitle.update('');
32087             tipTitle.hide();
32088         }
32089         el.dom.style.width  = tm.maxWidth+'px';
32090         //tipBody.dom.style.width = '';
32091         tipBodyText.update(o.text);
32092         var p = getPad(), w = ce.width;
32093         if(!w){
32094             var td = tipBodyText.dom;
32095             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32096             if(aw > tm.maxWidth){
32097                 w = tm.maxWidth;
32098             }else if(aw < tm.minWidth){
32099                 w = tm.minWidth;
32100             }else{
32101                 w = aw;
32102             }
32103         }
32104         //tipBody.setWidth(w);
32105         el.setWidth(parseInt(w, 10) + p);
32106         if(ce.autoHide === false){
32107             close.setDisplayed(true);
32108             if(dd){
32109                 dd.unlock();
32110             }
32111         }else{
32112             close.setDisplayed(false);
32113             if(dd){
32114                 dd.lock();
32115             }
32116         }
32117         if(xy){
32118             el.avoidY = xy[1]-18;
32119             el.setXY(xy);
32120         }
32121         if(tm.animate){
32122             el.setOpacity(.1);
32123             el.setStyle("visibility", "visible");
32124             el.fadeIn({callback: afterShow});
32125         }else{
32126             afterShow();
32127         }
32128     };
32129     
32130     var afterShow = function(){
32131         if(ce){
32132             el.show();
32133             esc.enable();
32134             if(tm.autoDismiss && ce.autoHide !== false){
32135                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32136             }
32137         }
32138     };
32139     
32140     var hide = function(noanim){
32141         clearTimeout(dismissProc);
32142         clearTimeout(hideProc);
32143         ce = null;
32144         if(el.isVisible()){
32145             esc.disable();
32146             if(noanim !== true && tm.animate){
32147                 el.fadeOut({callback: afterHide});
32148             }else{
32149                 afterHide();
32150             } 
32151         }
32152     };
32153     
32154     var afterHide = function(){
32155         el.hide();
32156         if(removeCls){
32157             el.removeClass(removeCls);
32158             removeCls = null;
32159         }
32160     };
32161     
32162     return {
32163         /**
32164         * @cfg {Number} minWidth
32165         * The minimum width of the quick tip (defaults to 40)
32166         */
32167        minWidth : 40,
32168         /**
32169         * @cfg {Number} maxWidth
32170         * The maximum width of the quick tip (defaults to 300)
32171         */
32172        maxWidth : 300,
32173         /**
32174         * @cfg {Boolean} interceptTitles
32175         * True to automatically use the element's DOM title value if available (defaults to false)
32176         */
32177        interceptTitles : false,
32178         /**
32179         * @cfg {Boolean} trackMouse
32180         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32181         */
32182        trackMouse : false,
32183         /**
32184         * @cfg {Boolean} hideOnClick
32185         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32186         */
32187        hideOnClick : true,
32188         /**
32189         * @cfg {Number} showDelay
32190         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32191         */
32192        showDelay : 500,
32193         /**
32194         * @cfg {Number} hideDelay
32195         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32196         */
32197        hideDelay : 200,
32198         /**
32199         * @cfg {Boolean} autoHide
32200         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32201         * Used in conjunction with hideDelay.
32202         */
32203        autoHide : true,
32204         /**
32205         * @cfg {Boolean}
32206         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32207         * (defaults to true).  Used in conjunction with autoDismissDelay.
32208         */
32209        autoDismiss : true,
32210         /**
32211         * @cfg {Number}
32212         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32213         */
32214        autoDismissDelay : 5000,
32215        /**
32216         * @cfg {Boolean} animate
32217         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32218         */
32219        animate : false,
32220
32221        /**
32222         * @cfg {String} title
32223         * Title text to display (defaults to '').  This can be any valid HTML markup.
32224         */
32225         title: '',
32226        /**
32227         * @cfg {String} text
32228         * Body text to display (defaults to '').  This can be any valid HTML markup.
32229         */
32230         text : '',
32231        /**
32232         * @cfg {String} cls
32233         * A CSS class to apply to the base quick tip element (defaults to '').
32234         */
32235         cls : '',
32236        /**
32237         * @cfg {Number} width
32238         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32239         * minWidth or maxWidth.
32240         */
32241         width : null,
32242
32243     /**
32244      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32245      * or display QuickTips in a page.
32246      */
32247        init : function(){
32248           tm = Roo.QuickTips;
32249           cfg = tm.tagConfig;
32250           if(!inited){
32251               if(!Roo.isReady){ // allow calling of init() before onReady
32252                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32253                   return;
32254               }
32255               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32256               el.fxDefaults = {stopFx: true};
32257               // maximum custom styling
32258               //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>');
32259               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>');              
32260               tipTitle = el.child('h3');
32261               tipTitle.enableDisplayMode("block");
32262               tipBody = el.child('div.x-tip-bd');
32263               tipBodyText = el.child('div.x-tip-bd-inner');
32264               //bdLeft = el.child('div.x-tip-bd-left');
32265               //bdRight = el.child('div.x-tip-bd-right');
32266               close = el.child('div.x-tip-close');
32267               close.enableDisplayMode("block");
32268               close.on("click", hide);
32269               var d = Roo.get(document);
32270               d.on("mousedown", onDown);
32271               d.on("mouseover", onOver);
32272               d.on("mouseout", onOut);
32273               d.on("mousemove", onMove);
32274               esc = d.addKeyListener(27, hide);
32275               esc.disable();
32276               if(Roo.dd.DD){
32277                   dd = el.initDD("default", null, {
32278                       onDrag : function(){
32279                           el.sync();  
32280                       }
32281                   });
32282                   dd.setHandleElId(tipTitle.id);
32283                   dd.lock();
32284               }
32285               inited = true;
32286           }
32287           this.enable(); 
32288        },
32289
32290     /**
32291      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32292      * are supported:
32293      * <pre>
32294 Property    Type                   Description
32295 ----------  ---------------------  ------------------------------------------------------------------------
32296 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32297      * </ul>
32298      * @param {Object} config The config object
32299      */
32300        register : function(config){
32301            var cs = config instanceof Array ? config : arguments;
32302            for(var i = 0, len = cs.length; i < len; i++) {
32303                var c = cs[i];
32304                var target = c.target;
32305                if(target){
32306                    if(target instanceof Array){
32307                        for(var j = 0, jlen = target.length; j < jlen; j++){
32308                            tagEls[target[j]] = c;
32309                        }
32310                    }else{
32311                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32312                    }
32313                }
32314            }
32315        },
32316
32317     /**
32318      * Removes this quick tip from its element and destroys it.
32319      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32320      */
32321        unregister : function(el){
32322            delete tagEls[Roo.id(el)];
32323        },
32324
32325     /**
32326      * Enable this quick tip.
32327      */
32328        enable : function(){
32329            if(inited && disabled){
32330                locks.pop();
32331                if(locks.length < 1){
32332                    disabled = false;
32333                }
32334            }
32335        },
32336
32337     /**
32338      * Disable this quick tip.
32339      */
32340        disable : function(){
32341           disabled = true;
32342           clearTimeout(showProc);
32343           clearTimeout(hideProc);
32344           clearTimeout(dismissProc);
32345           if(ce){
32346               hide(true);
32347           }
32348           locks.push(1);
32349        },
32350
32351     /**
32352      * Returns true if the quick tip is enabled, else false.
32353      */
32354        isEnabled : function(){
32355             return !disabled;
32356        },
32357
32358         // private
32359        tagConfig : {
32360            namespace : "ext",
32361            attribute : "qtip",
32362            width : "width",
32363            target : "target",
32364            title : "qtitle",
32365            hide : "hide",
32366            cls : "qclass"
32367        }
32368    };
32369 }();
32370
32371 // backwards compat
32372 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32373  * Based on:
32374  * Ext JS Library 1.1.1
32375  * Copyright(c) 2006-2007, Ext JS, LLC.
32376  *
32377  * Originally Released Under LGPL - original licence link has changed is not relivant.
32378  *
32379  * Fork - LGPL
32380  * <script type="text/javascript">
32381  */
32382  
32383
32384 /**
32385  * @class Roo.tree.TreePanel
32386  * @extends Roo.data.Tree
32387
32388  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32389  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32390  * @cfg {Boolean} enableDD true to enable drag and drop
32391  * @cfg {Boolean} enableDrag true to enable just drag
32392  * @cfg {Boolean} enableDrop true to enable just drop
32393  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32394  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32395  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32396  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32397  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32398  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32399  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32400  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32401  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32402  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32403  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32404  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32405  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32406  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32407  * @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>
32408  * @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>
32409  * 
32410  * @constructor
32411  * @param {String/HTMLElement/Element} el The container element
32412  * @param {Object} config
32413  */
32414 Roo.tree.TreePanel = function(el, config){
32415     var root = false;
32416     var loader = false;
32417     if (config.root) {
32418         root = config.root;
32419         delete config.root;
32420     }
32421     if (config.loader) {
32422         loader = config.loader;
32423         delete config.loader;
32424     }
32425     
32426     Roo.apply(this, config);
32427     Roo.tree.TreePanel.superclass.constructor.call(this);
32428     this.el = Roo.get(el);
32429     this.el.addClass('x-tree');
32430     //console.log(root);
32431     if (root) {
32432         this.setRootNode( Roo.factory(root, Roo.tree));
32433     }
32434     if (loader) {
32435         this.loader = Roo.factory(loader, Roo.tree);
32436     }
32437    /**
32438     * Read-only. The id of the container element becomes this TreePanel's id.
32439     */
32440     this.id = this.el.id;
32441     this.addEvents({
32442         /**
32443         * @event beforeload
32444         * Fires before a node is loaded, return false to cancel
32445         * @param {Node} node The node being loaded
32446         */
32447         "beforeload" : true,
32448         /**
32449         * @event load
32450         * Fires when a node is loaded
32451         * @param {Node} node The node that was loaded
32452         */
32453         "load" : true,
32454         /**
32455         * @event textchange
32456         * Fires when the text for a node is changed
32457         * @param {Node} node The node
32458         * @param {String} text The new text
32459         * @param {String} oldText The old text
32460         */
32461         "textchange" : true,
32462         /**
32463         * @event beforeexpand
32464         * Fires before a node is expanded, return false to cancel.
32465         * @param {Node} node The node
32466         * @param {Boolean} deep
32467         * @param {Boolean} anim
32468         */
32469         "beforeexpand" : true,
32470         /**
32471         * @event beforecollapse
32472         * Fires before a node is collapsed, return false to cancel.
32473         * @param {Node} node The node
32474         * @param {Boolean} deep
32475         * @param {Boolean} anim
32476         */
32477         "beforecollapse" : true,
32478         /**
32479         * @event expand
32480         * Fires when a node is expanded
32481         * @param {Node} node The node
32482         */
32483         "expand" : true,
32484         /**
32485         * @event disabledchange
32486         * Fires when the disabled status of a node changes
32487         * @param {Node} node The node
32488         * @param {Boolean} disabled
32489         */
32490         "disabledchange" : true,
32491         /**
32492         * @event collapse
32493         * Fires when a node is collapsed
32494         * @param {Node} node The node
32495         */
32496         "collapse" : true,
32497         /**
32498         * @event beforeclick
32499         * Fires before click processing on a node. Return false to cancel the default action.
32500         * @param {Node} node The node
32501         * @param {Roo.EventObject} e The event object
32502         */
32503         "beforeclick":true,
32504         /**
32505         * @event checkchange
32506         * Fires when a node with a checkbox's checked property changes
32507         * @param {Node} this This node
32508         * @param {Boolean} checked
32509         */
32510         "checkchange":true,
32511         /**
32512         * @event click
32513         * Fires when a node is clicked
32514         * @param {Node} node The node
32515         * @param {Roo.EventObject} e The event object
32516         */
32517         "click":true,
32518         /**
32519         * @event dblclick
32520         * Fires when a node is double clicked
32521         * @param {Node} node The node
32522         * @param {Roo.EventObject} e The event object
32523         */
32524         "dblclick":true,
32525         /**
32526         * @event contextmenu
32527         * Fires when a node is right clicked
32528         * @param {Node} node The node
32529         * @param {Roo.EventObject} e The event object
32530         */
32531         "contextmenu":true,
32532         /**
32533         * @event beforechildrenrendered
32534         * Fires right before the child nodes for a node are rendered
32535         * @param {Node} node The node
32536         */
32537         "beforechildrenrendered":true,
32538         /**
32539         * @event startdrag
32540         * Fires when a node starts being dragged
32541         * @param {Roo.tree.TreePanel} this
32542         * @param {Roo.tree.TreeNode} node
32543         * @param {event} e The raw browser event
32544         */ 
32545        "startdrag" : true,
32546        /**
32547         * @event enddrag
32548         * Fires when a drag operation is complete
32549         * @param {Roo.tree.TreePanel} this
32550         * @param {Roo.tree.TreeNode} node
32551         * @param {event} e The raw browser event
32552         */
32553        "enddrag" : true,
32554        /**
32555         * @event dragdrop
32556         * Fires when a dragged node is dropped on a valid DD target
32557         * @param {Roo.tree.TreePanel} this
32558         * @param {Roo.tree.TreeNode} node
32559         * @param {DD} dd The dd it was dropped on
32560         * @param {event} e The raw browser event
32561         */
32562        "dragdrop" : true,
32563        /**
32564         * @event beforenodedrop
32565         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32566         * passed to handlers has the following properties:<br />
32567         * <ul style="padding:5px;padding-left:16px;">
32568         * <li>tree - The TreePanel</li>
32569         * <li>target - The node being targeted for the drop</li>
32570         * <li>data - The drag data from the drag source</li>
32571         * <li>point - The point of the drop - append, above or below</li>
32572         * <li>source - The drag source</li>
32573         * <li>rawEvent - Raw mouse event</li>
32574         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32575         * to be inserted by setting them on this object.</li>
32576         * <li>cancel - Set this to true to cancel the drop.</li>
32577         * </ul>
32578         * @param {Object} dropEvent
32579         */
32580        "beforenodedrop" : true,
32581        /**
32582         * @event nodedrop
32583         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32584         * passed to handlers has the following properties:<br />
32585         * <ul style="padding:5px;padding-left:16px;">
32586         * <li>tree - The TreePanel</li>
32587         * <li>target - The node being targeted for the drop</li>
32588         * <li>data - The drag data from the drag source</li>
32589         * <li>point - The point of the drop - append, above or below</li>
32590         * <li>source - The drag source</li>
32591         * <li>rawEvent - Raw mouse event</li>
32592         * <li>dropNode - Dropped node(s).</li>
32593         * </ul>
32594         * @param {Object} dropEvent
32595         */
32596        "nodedrop" : true,
32597         /**
32598         * @event nodedragover
32599         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32600         * passed to handlers has the following properties:<br />
32601         * <ul style="padding:5px;padding-left:16px;">
32602         * <li>tree - The TreePanel</li>
32603         * <li>target - The node being targeted for the drop</li>
32604         * <li>data - The drag data from the drag source</li>
32605         * <li>point - The point of the drop - append, above or below</li>
32606         * <li>source - The drag source</li>
32607         * <li>rawEvent - Raw mouse event</li>
32608         * <li>dropNode - Drop node(s) provided by the source.</li>
32609         * <li>cancel - Set this to true to signal drop not allowed.</li>
32610         * </ul>
32611         * @param {Object} dragOverEvent
32612         */
32613        "nodedragover" : true
32614         
32615     });
32616     if(this.singleExpand){
32617        this.on("beforeexpand", this.restrictExpand, this);
32618     }
32619     if (this.editor) {
32620         this.editor.tree = this;
32621         this.editor = Roo.factory(this.editor, Roo.tree);
32622     }
32623     
32624     if (this.selModel) {
32625         this.selModel = Roo.factory(this.selModel, Roo.tree);
32626     }
32627    
32628 };
32629 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32630     rootVisible : true,
32631     animate: Roo.enableFx,
32632     lines : true,
32633     enableDD : false,
32634     hlDrop : Roo.enableFx,
32635   
32636     renderer: false,
32637     
32638     rendererTip: false,
32639     // private
32640     restrictExpand : function(node){
32641         var p = node.parentNode;
32642         if(p){
32643             if(p.expandedChild && p.expandedChild.parentNode == p){
32644                 p.expandedChild.collapse();
32645             }
32646             p.expandedChild = node;
32647         }
32648     },
32649
32650     // private override
32651     setRootNode : function(node){
32652         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32653         if(!this.rootVisible){
32654             node.ui = new Roo.tree.RootTreeNodeUI(node);
32655         }
32656         return node;
32657     },
32658
32659     /**
32660      * Returns the container element for this TreePanel
32661      */
32662     getEl : function(){
32663         return this.el;
32664     },
32665
32666     /**
32667      * Returns the default TreeLoader for this TreePanel
32668      */
32669     getLoader : function(){
32670         return this.loader;
32671     },
32672
32673     /**
32674      * Expand all nodes
32675      */
32676     expandAll : function(){
32677         this.root.expand(true);
32678     },
32679
32680     /**
32681      * Collapse all nodes
32682      */
32683     collapseAll : function(){
32684         this.root.collapse(true);
32685     },
32686
32687     /**
32688      * Returns the selection model used by this TreePanel
32689      */
32690     getSelectionModel : function(){
32691         if(!this.selModel){
32692             this.selModel = new Roo.tree.DefaultSelectionModel();
32693         }
32694         return this.selModel;
32695     },
32696
32697     /**
32698      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32699      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32700      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32701      * @return {Array}
32702      */
32703     getChecked : function(a, startNode){
32704         startNode = startNode || this.root;
32705         var r = [];
32706         var f = function(){
32707             if(this.attributes.checked){
32708                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32709             }
32710         }
32711         startNode.cascade(f);
32712         return r;
32713     },
32714
32715     /**
32716      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32717      * @param {String} path
32718      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32719      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32720      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32721      */
32722     expandPath : function(path, attr, callback){
32723         attr = attr || "id";
32724         var keys = path.split(this.pathSeparator);
32725         var curNode = this.root;
32726         if(curNode.attributes[attr] != keys[1]){ // invalid root
32727             if(callback){
32728                 callback(false, null);
32729             }
32730             return;
32731         }
32732         var index = 1;
32733         var f = function(){
32734             if(++index == keys.length){
32735                 if(callback){
32736                     callback(true, curNode);
32737                 }
32738                 return;
32739             }
32740             var c = curNode.findChild(attr, keys[index]);
32741             if(!c){
32742                 if(callback){
32743                     callback(false, curNode);
32744                 }
32745                 return;
32746             }
32747             curNode = c;
32748             c.expand(false, false, f);
32749         };
32750         curNode.expand(false, false, f);
32751     },
32752
32753     /**
32754      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32755      * @param {String} path
32756      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32757      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32758      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32759      */
32760     selectPath : function(path, attr, callback){
32761         attr = attr || "id";
32762         var keys = path.split(this.pathSeparator);
32763         var v = keys.pop();
32764         if(keys.length > 0){
32765             var f = function(success, node){
32766                 if(success && node){
32767                     var n = node.findChild(attr, v);
32768                     if(n){
32769                         n.select();
32770                         if(callback){
32771                             callback(true, n);
32772                         }
32773                     }else if(callback){
32774                         callback(false, n);
32775                     }
32776                 }else{
32777                     if(callback){
32778                         callback(false, n);
32779                     }
32780                 }
32781             };
32782             this.expandPath(keys.join(this.pathSeparator), attr, f);
32783         }else{
32784             this.root.select();
32785             if(callback){
32786                 callback(true, this.root);
32787             }
32788         }
32789     },
32790
32791     getTreeEl : function(){
32792         return this.el;
32793     },
32794
32795     /**
32796      * Trigger rendering of this TreePanel
32797      */
32798     render : function(){
32799         if (this.innerCt) {
32800             return this; // stop it rendering more than once!!
32801         }
32802         
32803         this.innerCt = this.el.createChild({tag:"ul",
32804                cls:"x-tree-root-ct " +
32805                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32806
32807         if(this.containerScroll){
32808             Roo.dd.ScrollManager.register(this.el);
32809         }
32810         if((this.enableDD || this.enableDrop) && !this.dropZone){
32811            /**
32812             * The dropZone used by this tree if drop is enabled
32813             * @type Roo.tree.TreeDropZone
32814             */
32815              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32816                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32817            });
32818         }
32819         if((this.enableDD || this.enableDrag) && !this.dragZone){
32820            /**
32821             * The dragZone used by this tree if drag is enabled
32822             * @type Roo.tree.TreeDragZone
32823             */
32824             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32825                ddGroup: this.ddGroup || "TreeDD",
32826                scroll: this.ddScroll
32827            });
32828         }
32829         this.getSelectionModel().init(this);
32830         if (!this.root) {
32831             Roo.log("ROOT not set in tree");
32832             return this;
32833         }
32834         this.root.render();
32835         if(!this.rootVisible){
32836             this.root.renderChildren();
32837         }
32838         return this;
32839     }
32840 });/*
32841  * Based on:
32842  * Ext JS Library 1.1.1
32843  * Copyright(c) 2006-2007, Ext JS, LLC.
32844  *
32845  * Originally Released Under LGPL - original licence link has changed is not relivant.
32846  *
32847  * Fork - LGPL
32848  * <script type="text/javascript">
32849  */
32850  
32851
32852 /**
32853  * @class Roo.tree.DefaultSelectionModel
32854  * @extends Roo.util.Observable
32855  * The default single selection for a TreePanel.
32856  * @param {Object} cfg Configuration
32857  */
32858 Roo.tree.DefaultSelectionModel = function(cfg){
32859    this.selNode = null;
32860    
32861    
32862    
32863    this.addEvents({
32864        /**
32865         * @event selectionchange
32866         * Fires when the selected node changes
32867         * @param {DefaultSelectionModel} this
32868         * @param {TreeNode} node the new selection
32869         */
32870        "selectionchange" : true,
32871
32872        /**
32873         * @event beforeselect
32874         * Fires before the selected node changes, return false to cancel the change
32875         * @param {DefaultSelectionModel} this
32876         * @param {TreeNode} node the new selection
32877         * @param {TreeNode} node the old selection
32878         */
32879        "beforeselect" : true
32880    });
32881    
32882     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32883 };
32884
32885 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32886     init : function(tree){
32887         this.tree = tree;
32888         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32889         tree.on("click", this.onNodeClick, this);
32890     },
32891     
32892     onNodeClick : function(node, e){
32893         if (e.ctrlKey && this.selNode == node)  {
32894             this.unselect(node);
32895             return;
32896         }
32897         this.select(node);
32898     },
32899     
32900     /**
32901      * Select a node.
32902      * @param {TreeNode} node The node to select
32903      * @return {TreeNode} The selected node
32904      */
32905     select : function(node){
32906         var last = this.selNode;
32907         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32908             if(last){
32909                 last.ui.onSelectedChange(false);
32910             }
32911             this.selNode = node;
32912             node.ui.onSelectedChange(true);
32913             this.fireEvent("selectionchange", this, node, last);
32914         }
32915         return node;
32916     },
32917     
32918     /**
32919      * Deselect a node.
32920      * @param {TreeNode} node The node to unselect
32921      */
32922     unselect : function(node){
32923         if(this.selNode == node){
32924             this.clearSelections();
32925         }    
32926     },
32927     
32928     /**
32929      * Clear all selections
32930      */
32931     clearSelections : function(){
32932         var n = this.selNode;
32933         if(n){
32934             n.ui.onSelectedChange(false);
32935             this.selNode = null;
32936             this.fireEvent("selectionchange", this, null);
32937         }
32938         return n;
32939     },
32940     
32941     /**
32942      * Get the selected node
32943      * @return {TreeNode} The selected node
32944      */
32945     getSelectedNode : function(){
32946         return this.selNode;    
32947     },
32948     
32949     /**
32950      * Returns true if the node is selected
32951      * @param {TreeNode} node The node to check
32952      * @return {Boolean}
32953      */
32954     isSelected : function(node){
32955         return this.selNode == node;  
32956     },
32957
32958     /**
32959      * Selects the node above the selected node in the tree, intelligently walking the nodes
32960      * @return TreeNode The new selection
32961      */
32962     selectPrevious : function(){
32963         var s = this.selNode || this.lastSelNode;
32964         if(!s){
32965             return null;
32966         }
32967         var ps = s.previousSibling;
32968         if(ps){
32969             if(!ps.isExpanded() || ps.childNodes.length < 1){
32970                 return this.select(ps);
32971             } else{
32972                 var lc = ps.lastChild;
32973                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32974                     lc = lc.lastChild;
32975                 }
32976                 return this.select(lc);
32977             }
32978         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32979             return this.select(s.parentNode);
32980         }
32981         return null;
32982     },
32983
32984     /**
32985      * Selects the node above the selected node in the tree, intelligently walking the nodes
32986      * @return TreeNode The new selection
32987      */
32988     selectNext : function(){
32989         var s = this.selNode || this.lastSelNode;
32990         if(!s){
32991             return null;
32992         }
32993         if(s.firstChild && s.isExpanded()){
32994              return this.select(s.firstChild);
32995          }else if(s.nextSibling){
32996              return this.select(s.nextSibling);
32997          }else if(s.parentNode){
32998             var newS = null;
32999             s.parentNode.bubble(function(){
33000                 if(this.nextSibling){
33001                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33002                     return false;
33003                 }
33004             });
33005             return newS;
33006          }
33007         return null;
33008     },
33009
33010     onKeyDown : function(e){
33011         var s = this.selNode || this.lastSelNode;
33012         // undesirable, but required
33013         var sm = this;
33014         if(!s){
33015             return;
33016         }
33017         var k = e.getKey();
33018         switch(k){
33019              case e.DOWN:
33020                  e.stopEvent();
33021                  this.selectNext();
33022              break;
33023              case e.UP:
33024                  e.stopEvent();
33025                  this.selectPrevious();
33026              break;
33027              case e.RIGHT:
33028                  e.preventDefault();
33029                  if(s.hasChildNodes()){
33030                      if(!s.isExpanded()){
33031                          s.expand();
33032                      }else if(s.firstChild){
33033                          this.select(s.firstChild, e);
33034                      }
33035                  }
33036              break;
33037              case e.LEFT:
33038                  e.preventDefault();
33039                  if(s.hasChildNodes() && s.isExpanded()){
33040                      s.collapse();
33041                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33042                      this.select(s.parentNode, e);
33043                  }
33044              break;
33045         };
33046     }
33047 });
33048
33049 /**
33050  * @class Roo.tree.MultiSelectionModel
33051  * @extends Roo.util.Observable
33052  * Multi selection for a TreePanel.
33053  * @param {Object} cfg Configuration
33054  */
33055 Roo.tree.MultiSelectionModel = function(){
33056    this.selNodes = [];
33057    this.selMap = {};
33058    this.addEvents({
33059        /**
33060         * @event selectionchange
33061         * Fires when the selected nodes change
33062         * @param {MultiSelectionModel} this
33063         * @param {Array} nodes Array of the selected nodes
33064         */
33065        "selectionchange" : true
33066    });
33067    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33068    
33069 };
33070
33071 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33072     init : function(tree){
33073         this.tree = tree;
33074         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33075         tree.on("click", this.onNodeClick, this);
33076     },
33077     
33078     onNodeClick : function(node, e){
33079         this.select(node, e, e.ctrlKey);
33080     },
33081     
33082     /**
33083      * Select a node.
33084      * @param {TreeNode} node The node to select
33085      * @param {EventObject} e (optional) An event associated with the selection
33086      * @param {Boolean} keepExisting True to retain existing selections
33087      * @return {TreeNode} The selected node
33088      */
33089     select : function(node, e, keepExisting){
33090         if(keepExisting !== true){
33091             this.clearSelections(true);
33092         }
33093         if(this.isSelected(node)){
33094             this.lastSelNode = node;
33095             return node;
33096         }
33097         this.selNodes.push(node);
33098         this.selMap[node.id] = node;
33099         this.lastSelNode = node;
33100         node.ui.onSelectedChange(true);
33101         this.fireEvent("selectionchange", this, this.selNodes);
33102         return node;
33103     },
33104     
33105     /**
33106      * Deselect a node.
33107      * @param {TreeNode} node The node to unselect
33108      */
33109     unselect : function(node){
33110         if(this.selMap[node.id]){
33111             node.ui.onSelectedChange(false);
33112             var sn = this.selNodes;
33113             var index = -1;
33114             if(sn.indexOf){
33115                 index = sn.indexOf(node);
33116             }else{
33117                 for(var i = 0, len = sn.length; i < len; i++){
33118                     if(sn[i] == node){
33119                         index = i;
33120                         break;
33121                     }
33122                 }
33123             }
33124             if(index != -1){
33125                 this.selNodes.splice(index, 1);
33126             }
33127             delete this.selMap[node.id];
33128             this.fireEvent("selectionchange", this, this.selNodes);
33129         }
33130     },
33131     
33132     /**
33133      * Clear all selections
33134      */
33135     clearSelections : function(suppressEvent){
33136         var sn = this.selNodes;
33137         if(sn.length > 0){
33138             for(var i = 0, len = sn.length; i < len; i++){
33139                 sn[i].ui.onSelectedChange(false);
33140             }
33141             this.selNodes = [];
33142             this.selMap = {};
33143             if(suppressEvent !== true){
33144                 this.fireEvent("selectionchange", this, this.selNodes);
33145             }
33146         }
33147     },
33148     
33149     /**
33150      * Returns true if the node is selected
33151      * @param {TreeNode} node The node to check
33152      * @return {Boolean}
33153      */
33154     isSelected : function(node){
33155         return this.selMap[node.id] ? true : false;  
33156     },
33157     
33158     /**
33159      * Returns an array of the selected nodes
33160      * @return {Array}
33161      */
33162     getSelectedNodes : function(){
33163         return this.selNodes;    
33164     },
33165
33166     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33167
33168     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33169
33170     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33171 });/*
33172  * Based on:
33173  * Ext JS Library 1.1.1
33174  * Copyright(c) 2006-2007, Ext JS, LLC.
33175  *
33176  * Originally Released Under LGPL - original licence link has changed is not relivant.
33177  *
33178  * Fork - LGPL
33179  * <script type="text/javascript">
33180  */
33181  
33182 /**
33183  * @class Roo.tree.TreeNode
33184  * @extends Roo.data.Node
33185  * @cfg {String} text The text for this node
33186  * @cfg {Boolean} expanded true to start the node expanded
33187  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33188  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33189  * @cfg {Boolean} disabled true to start the node disabled
33190  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33191  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33192  * @cfg {String} cls A css class to be added to the node
33193  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33194  * @cfg {String} href URL of the link used for the node (defaults to #)
33195  * @cfg {String} hrefTarget target frame for the link
33196  * @cfg {String} qtip An Ext QuickTip for the node
33197  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33198  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33199  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33200  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33201  * (defaults to undefined with no checkbox rendered)
33202  * @constructor
33203  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33204  */
33205 Roo.tree.TreeNode = function(attributes){
33206     attributes = attributes || {};
33207     if(typeof attributes == "string"){
33208         attributes = {text: attributes};
33209     }
33210     this.childrenRendered = false;
33211     this.rendered = false;
33212     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33213     this.expanded = attributes.expanded === true;
33214     this.isTarget = attributes.isTarget !== false;
33215     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33216     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33217
33218     /**
33219      * Read-only. The text for this node. To change it use setText().
33220      * @type String
33221      */
33222     this.text = attributes.text;
33223     /**
33224      * True if this node is disabled.
33225      * @type Boolean
33226      */
33227     this.disabled = attributes.disabled === true;
33228
33229     this.addEvents({
33230         /**
33231         * @event textchange
33232         * Fires when the text for this node is changed
33233         * @param {Node} this This node
33234         * @param {String} text The new text
33235         * @param {String} oldText The old text
33236         */
33237         "textchange" : true,
33238         /**
33239         * @event beforeexpand
33240         * Fires before this node is expanded, return false to cancel.
33241         * @param {Node} this This node
33242         * @param {Boolean} deep
33243         * @param {Boolean} anim
33244         */
33245         "beforeexpand" : true,
33246         /**
33247         * @event beforecollapse
33248         * Fires before this node is collapsed, return false to cancel.
33249         * @param {Node} this This node
33250         * @param {Boolean} deep
33251         * @param {Boolean} anim
33252         */
33253         "beforecollapse" : true,
33254         /**
33255         * @event expand
33256         * Fires when this node is expanded
33257         * @param {Node} this This node
33258         */
33259         "expand" : true,
33260         /**
33261         * @event disabledchange
33262         * Fires when the disabled status of this node changes
33263         * @param {Node} this This node
33264         * @param {Boolean} disabled
33265         */
33266         "disabledchange" : true,
33267         /**
33268         * @event collapse
33269         * Fires when this node is collapsed
33270         * @param {Node} this This node
33271         */
33272         "collapse" : true,
33273         /**
33274         * @event beforeclick
33275         * Fires before click processing. Return false to cancel the default action.
33276         * @param {Node} this This node
33277         * @param {Roo.EventObject} e The event object
33278         */
33279         "beforeclick":true,
33280         /**
33281         * @event checkchange
33282         * Fires when a node with a checkbox's checked property changes
33283         * @param {Node} this This node
33284         * @param {Boolean} checked
33285         */
33286         "checkchange":true,
33287         /**
33288         * @event click
33289         * Fires when this node is clicked
33290         * @param {Node} this This node
33291         * @param {Roo.EventObject} e The event object
33292         */
33293         "click":true,
33294         /**
33295         * @event dblclick
33296         * Fires when this node is double clicked
33297         * @param {Node} this This node
33298         * @param {Roo.EventObject} e The event object
33299         */
33300         "dblclick":true,
33301         /**
33302         * @event contextmenu
33303         * Fires when this node is right clicked
33304         * @param {Node} this This node
33305         * @param {Roo.EventObject} e The event object
33306         */
33307         "contextmenu":true,
33308         /**
33309         * @event beforechildrenrendered
33310         * Fires right before the child nodes for this node are rendered
33311         * @param {Node} this This node
33312         */
33313         "beforechildrenrendered":true
33314     });
33315
33316     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33317
33318     /**
33319      * Read-only. The UI for this node
33320      * @type TreeNodeUI
33321      */
33322     this.ui = new uiClass(this);
33323     
33324     // finally support items[]
33325     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33326         return;
33327     }
33328     
33329     
33330     Roo.each(this.attributes.items, function(c) {
33331         this.appendChild(Roo.factory(c,Roo.Tree));
33332     }, this);
33333     delete this.attributes.items;
33334     
33335     
33336     
33337 };
33338 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33339     preventHScroll: true,
33340     /**
33341      * Returns true if this node is expanded
33342      * @return {Boolean}
33343      */
33344     isExpanded : function(){
33345         return this.expanded;
33346     },
33347
33348     /**
33349      * Returns the UI object for this node
33350      * @return {TreeNodeUI}
33351      */
33352     getUI : function(){
33353         return this.ui;
33354     },
33355
33356     // private override
33357     setFirstChild : function(node){
33358         var of = this.firstChild;
33359         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33360         if(this.childrenRendered && of && node != of){
33361             of.renderIndent(true, true);
33362         }
33363         if(this.rendered){
33364             this.renderIndent(true, true);
33365         }
33366     },
33367
33368     // private override
33369     setLastChild : function(node){
33370         var ol = this.lastChild;
33371         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33372         if(this.childrenRendered && ol && node != ol){
33373             ol.renderIndent(true, true);
33374         }
33375         if(this.rendered){
33376             this.renderIndent(true, true);
33377         }
33378     },
33379
33380     // these methods are overridden to provide lazy rendering support
33381     // private override
33382     appendChild : function()
33383     {
33384         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33385         if(node && this.childrenRendered){
33386             node.render();
33387         }
33388         this.ui.updateExpandIcon();
33389         return node;
33390     },
33391
33392     // private override
33393     removeChild : function(node){
33394         this.ownerTree.getSelectionModel().unselect(node);
33395         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33396         // if it's been rendered remove dom node
33397         if(this.childrenRendered){
33398             node.ui.remove();
33399         }
33400         if(this.childNodes.length < 1){
33401             this.collapse(false, false);
33402         }else{
33403             this.ui.updateExpandIcon();
33404         }
33405         if(!this.firstChild) {
33406             this.childrenRendered = false;
33407         }
33408         return node;
33409     },
33410
33411     // private override
33412     insertBefore : function(node, refNode){
33413         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33414         if(newNode && refNode && this.childrenRendered){
33415             node.render();
33416         }
33417         this.ui.updateExpandIcon();
33418         return newNode;
33419     },
33420
33421     /**
33422      * Sets the text for this node
33423      * @param {String} text
33424      */
33425     setText : function(text){
33426         var oldText = this.text;
33427         this.text = text;
33428         this.attributes.text = text;
33429         if(this.rendered){ // event without subscribing
33430             this.ui.onTextChange(this, text, oldText);
33431         }
33432         this.fireEvent("textchange", this, text, oldText);
33433     },
33434
33435     /**
33436      * Triggers selection of this node
33437      */
33438     select : function(){
33439         this.getOwnerTree().getSelectionModel().select(this);
33440     },
33441
33442     /**
33443      * Triggers deselection of this node
33444      */
33445     unselect : function(){
33446         this.getOwnerTree().getSelectionModel().unselect(this);
33447     },
33448
33449     /**
33450      * Returns true if this node is selected
33451      * @return {Boolean}
33452      */
33453     isSelected : function(){
33454         return this.getOwnerTree().getSelectionModel().isSelected(this);
33455     },
33456
33457     /**
33458      * Expand this node.
33459      * @param {Boolean} deep (optional) True to expand all children as well
33460      * @param {Boolean} anim (optional) false to cancel the default animation
33461      * @param {Function} callback (optional) A callback to be called when
33462      * expanding this node completes (does not wait for deep expand to complete).
33463      * Called with 1 parameter, this node.
33464      */
33465     expand : function(deep, anim, callback){
33466         if(!this.expanded){
33467             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33468                 return;
33469             }
33470             if(!this.childrenRendered){
33471                 this.renderChildren();
33472             }
33473             this.expanded = true;
33474             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33475                 this.ui.animExpand(function(){
33476                     this.fireEvent("expand", this);
33477                     if(typeof callback == "function"){
33478                         callback(this);
33479                     }
33480                     if(deep === true){
33481                         this.expandChildNodes(true);
33482                     }
33483                 }.createDelegate(this));
33484                 return;
33485             }else{
33486                 this.ui.expand();
33487                 this.fireEvent("expand", this);
33488                 if(typeof callback == "function"){
33489                     callback(this);
33490                 }
33491             }
33492         }else{
33493            if(typeof callback == "function"){
33494                callback(this);
33495            }
33496         }
33497         if(deep === true){
33498             this.expandChildNodes(true);
33499         }
33500     },
33501
33502     isHiddenRoot : function(){
33503         return this.isRoot && !this.getOwnerTree().rootVisible;
33504     },
33505
33506     /**
33507      * Collapse this node.
33508      * @param {Boolean} deep (optional) True to collapse all children as well
33509      * @param {Boolean} anim (optional) false to cancel the default animation
33510      */
33511     collapse : function(deep, anim){
33512         if(this.expanded && !this.isHiddenRoot()){
33513             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33514                 return;
33515             }
33516             this.expanded = false;
33517             if((this.getOwnerTree().animate && anim !== false) || anim){
33518                 this.ui.animCollapse(function(){
33519                     this.fireEvent("collapse", this);
33520                     if(deep === true){
33521                         this.collapseChildNodes(true);
33522                     }
33523                 }.createDelegate(this));
33524                 return;
33525             }else{
33526                 this.ui.collapse();
33527                 this.fireEvent("collapse", this);
33528             }
33529         }
33530         if(deep === true){
33531             var cs = this.childNodes;
33532             for(var i = 0, len = cs.length; i < len; i++) {
33533                 cs[i].collapse(true, false);
33534             }
33535         }
33536     },
33537
33538     // private
33539     delayedExpand : function(delay){
33540         if(!this.expandProcId){
33541             this.expandProcId = this.expand.defer(delay, this);
33542         }
33543     },
33544
33545     // private
33546     cancelExpand : function(){
33547         if(this.expandProcId){
33548             clearTimeout(this.expandProcId);
33549         }
33550         this.expandProcId = false;
33551     },
33552
33553     /**
33554      * Toggles expanded/collapsed state of the node
33555      */
33556     toggle : function(){
33557         if(this.expanded){
33558             this.collapse();
33559         }else{
33560             this.expand();
33561         }
33562     },
33563
33564     /**
33565      * Ensures all parent nodes are expanded
33566      */
33567     ensureVisible : function(callback){
33568         var tree = this.getOwnerTree();
33569         tree.expandPath(this.parentNode.getPath(), false, function(){
33570             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33571             Roo.callback(callback);
33572         }.createDelegate(this));
33573     },
33574
33575     /**
33576      * Expand all child nodes
33577      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33578      */
33579     expandChildNodes : function(deep){
33580         var cs = this.childNodes;
33581         for(var i = 0, len = cs.length; i < len; i++) {
33582                 cs[i].expand(deep);
33583         }
33584     },
33585
33586     /**
33587      * Collapse all child nodes
33588      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33589      */
33590     collapseChildNodes : function(deep){
33591         var cs = this.childNodes;
33592         for(var i = 0, len = cs.length; i < len; i++) {
33593                 cs[i].collapse(deep);
33594         }
33595     },
33596
33597     /**
33598      * Disables this node
33599      */
33600     disable : function(){
33601         this.disabled = true;
33602         this.unselect();
33603         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33604             this.ui.onDisableChange(this, true);
33605         }
33606         this.fireEvent("disabledchange", this, true);
33607     },
33608
33609     /**
33610      * Enables this node
33611      */
33612     enable : function(){
33613         this.disabled = false;
33614         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33615             this.ui.onDisableChange(this, false);
33616         }
33617         this.fireEvent("disabledchange", this, false);
33618     },
33619
33620     // private
33621     renderChildren : function(suppressEvent){
33622         if(suppressEvent !== false){
33623             this.fireEvent("beforechildrenrendered", this);
33624         }
33625         var cs = this.childNodes;
33626         for(var i = 0, len = cs.length; i < len; i++){
33627             cs[i].render(true);
33628         }
33629         this.childrenRendered = true;
33630     },
33631
33632     // private
33633     sort : function(fn, scope){
33634         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33635         if(this.childrenRendered){
33636             var cs = this.childNodes;
33637             for(var i = 0, len = cs.length; i < len; i++){
33638                 cs[i].render(true);
33639             }
33640         }
33641     },
33642
33643     // private
33644     render : function(bulkRender){
33645         this.ui.render(bulkRender);
33646         if(!this.rendered){
33647             this.rendered = true;
33648             if(this.expanded){
33649                 this.expanded = false;
33650                 this.expand(false, false);
33651             }
33652         }
33653     },
33654
33655     // private
33656     renderIndent : function(deep, refresh){
33657         if(refresh){
33658             this.ui.childIndent = null;
33659         }
33660         this.ui.renderIndent();
33661         if(deep === true && this.childrenRendered){
33662             var cs = this.childNodes;
33663             for(var i = 0, len = cs.length; i < len; i++){
33664                 cs[i].renderIndent(true, refresh);
33665             }
33666         }
33667     }
33668 });/*
33669  * Based on:
33670  * Ext JS Library 1.1.1
33671  * Copyright(c) 2006-2007, Ext JS, LLC.
33672  *
33673  * Originally Released Under LGPL - original licence link has changed is not relivant.
33674  *
33675  * Fork - LGPL
33676  * <script type="text/javascript">
33677  */
33678  
33679 /**
33680  * @class Roo.tree.AsyncTreeNode
33681  * @extends Roo.tree.TreeNode
33682  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33683  * @constructor
33684  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33685  */
33686  Roo.tree.AsyncTreeNode = function(config){
33687     this.loaded = false;
33688     this.loading = false;
33689     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33690     /**
33691     * @event beforeload
33692     * Fires before this node is loaded, return false to cancel
33693     * @param {Node} this This node
33694     */
33695     this.addEvents({'beforeload':true, 'load': true});
33696     /**
33697     * @event load
33698     * Fires when this node is loaded
33699     * @param {Node} this This node
33700     */
33701     /**
33702      * The loader used by this node (defaults to using the tree's defined loader)
33703      * @type TreeLoader
33704      * @property loader
33705      */
33706 };
33707 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33708     expand : function(deep, anim, callback){
33709         if(this.loading){ // if an async load is already running, waiting til it's done
33710             var timer;
33711             var f = function(){
33712                 if(!this.loading){ // done loading
33713                     clearInterval(timer);
33714                     this.expand(deep, anim, callback);
33715                 }
33716             }.createDelegate(this);
33717             timer = setInterval(f, 200);
33718             return;
33719         }
33720         if(!this.loaded){
33721             if(this.fireEvent("beforeload", this) === false){
33722                 return;
33723             }
33724             this.loading = true;
33725             this.ui.beforeLoad(this);
33726             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33727             if(loader){
33728                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33729                 return;
33730             }
33731         }
33732         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33733     },
33734     
33735     /**
33736      * Returns true if this node is currently loading
33737      * @return {Boolean}
33738      */
33739     isLoading : function(){
33740         return this.loading;  
33741     },
33742     
33743     loadComplete : function(deep, anim, callback){
33744         this.loading = false;
33745         this.loaded = true;
33746         this.ui.afterLoad(this);
33747         this.fireEvent("load", this);
33748         this.expand(deep, anim, callback);
33749     },
33750     
33751     /**
33752      * Returns true if this node has been loaded
33753      * @return {Boolean}
33754      */
33755     isLoaded : function(){
33756         return this.loaded;
33757     },
33758     
33759     hasChildNodes : function(){
33760         if(!this.isLeaf() && !this.loaded){
33761             return true;
33762         }else{
33763             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33764         }
33765     },
33766
33767     /**
33768      * Trigger a reload for this node
33769      * @param {Function} callback
33770      */
33771     reload : function(callback){
33772         this.collapse(false, false);
33773         while(this.firstChild){
33774             this.removeChild(this.firstChild);
33775         }
33776         this.childrenRendered = false;
33777         this.loaded = false;
33778         if(this.isHiddenRoot()){
33779             this.expanded = false;
33780         }
33781         this.expand(false, false, callback);
33782     }
33783 });/*
33784  * Based on:
33785  * Ext JS Library 1.1.1
33786  * Copyright(c) 2006-2007, Ext JS, LLC.
33787  *
33788  * Originally Released Under LGPL - original licence link has changed is not relivant.
33789  *
33790  * Fork - LGPL
33791  * <script type="text/javascript">
33792  */
33793  
33794 /**
33795  * @class Roo.tree.TreeNodeUI
33796  * @constructor
33797  * @param {Object} node The node to render
33798  * The TreeNode UI implementation is separate from the
33799  * tree implementation. Unless you are customizing the tree UI,
33800  * you should never have to use this directly.
33801  */
33802 Roo.tree.TreeNodeUI = function(node){
33803     this.node = node;
33804     this.rendered = false;
33805     this.animating = false;
33806     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33807 };
33808
33809 Roo.tree.TreeNodeUI.prototype = {
33810     removeChild : function(node){
33811         if(this.rendered){
33812             this.ctNode.removeChild(node.ui.getEl());
33813         }
33814     },
33815
33816     beforeLoad : function(){
33817          this.addClass("x-tree-node-loading");
33818     },
33819
33820     afterLoad : function(){
33821          this.removeClass("x-tree-node-loading");
33822     },
33823
33824     onTextChange : function(node, text, oldText){
33825         if(this.rendered){
33826             this.textNode.innerHTML = text;
33827         }
33828     },
33829
33830     onDisableChange : function(node, state){
33831         this.disabled = state;
33832         if(state){
33833             this.addClass("x-tree-node-disabled");
33834         }else{
33835             this.removeClass("x-tree-node-disabled");
33836         }
33837     },
33838
33839     onSelectedChange : function(state){
33840         if(state){
33841             this.focus();
33842             this.addClass("x-tree-selected");
33843         }else{
33844             //this.blur();
33845             this.removeClass("x-tree-selected");
33846         }
33847     },
33848
33849     onMove : function(tree, node, oldParent, newParent, index, refNode){
33850         this.childIndent = null;
33851         if(this.rendered){
33852             var targetNode = newParent.ui.getContainer();
33853             if(!targetNode){//target not rendered
33854                 this.holder = document.createElement("div");
33855                 this.holder.appendChild(this.wrap);
33856                 return;
33857             }
33858             var insertBefore = refNode ? refNode.ui.getEl() : null;
33859             if(insertBefore){
33860                 targetNode.insertBefore(this.wrap, insertBefore);
33861             }else{
33862                 targetNode.appendChild(this.wrap);
33863             }
33864             this.node.renderIndent(true);
33865         }
33866     },
33867
33868     addClass : function(cls){
33869         if(this.elNode){
33870             Roo.fly(this.elNode).addClass(cls);
33871         }
33872     },
33873
33874     removeClass : function(cls){
33875         if(this.elNode){
33876             Roo.fly(this.elNode).removeClass(cls);
33877         }
33878     },
33879
33880     remove : function(){
33881         if(this.rendered){
33882             this.holder = document.createElement("div");
33883             this.holder.appendChild(this.wrap);
33884         }
33885     },
33886
33887     fireEvent : function(){
33888         return this.node.fireEvent.apply(this.node, arguments);
33889     },
33890
33891     initEvents : function(){
33892         this.node.on("move", this.onMove, this);
33893         var E = Roo.EventManager;
33894         var a = this.anchor;
33895
33896         var el = Roo.fly(a, '_treeui');
33897
33898         if(Roo.isOpera){ // opera render bug ignores the CSS
33899             el.setStyle("text-decoration", "none");
33900         }
33901
33902         el.on("click", this.onClick, this);
33903         el.on("dblclick", this.onDblClick, this);
33904
33905         if(this.checkbox){
33906             Roo.EventManager.on(this.checkbox,
33907                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33908         }
33909
33910         el.on("contextmenu", this.onContextMenu, this);
33911
33912         var icon = Roo.fly(this.iconNode);
33913         icon.on("click", this.onClick, this);
33914         icon.on("dblclick", this.onDblClick, this);
33915         icon.on("contextmenu", this.onContextMenu, this);
33916         E.on(this.ecNode, "click", this.ecClick, this, true);
33917
33918         if(this.node.disabled){
33919             this.addClass("x-tree-node-disabled");
33920         }
33921         if(this.node.hidden){
33922             this.addClass("x-tree-node-disabled");
33923         }
33924         var ot = this.node.getOwnerTree();
33925         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33926         if(dd && (!this.node.isRoot || ot.rootVisible)){
33927             Roo.dd.Registry.register(this.elNode, {
33928                 node: this.node,
33929                 handles: this.getDDHandles(),
33930                 isHandle: false
33931             });
33932         }
33933     },
33934
33935     getDDHandles : function(){
33936         return [this.iconNode, this.textNode];
33937     },
33938
33939     hide : function(){
33940         if(this.rendered){
33941             this.wrap.style.display = "none";
33942         }
33943     },
33944
33945     show : function(){
33946         if(this.rendered){
33947             this.wrap.style.display = "";
33948         }
33949     },
33950
33951     onContextMenu : function(e){
33952         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33953             e.preventDefault();
33954             this.focus();
33955             this.fireEvent("contextmenu", this.node, e);
33956         }
33957     },
33958
33959     onClick : function(e){
33960         if(this.dropping){
33961             e.stopEvent();
33962             return;
33963         }
33964         if(this.fireEvent("beforeclick", this.node, e) !== false){
33965             if(!this.disabled && this.node.attributes.href){
33966                 this.fireEvent("click", this.node, e);
33967                 return;
33968             }
33969             e.preventDefault();
33970             if(this.disabled){
33971                 return;
33972             }
33973
33974             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33975                 this.node.toggle();
33976             }
33977
33978             this.fireEvent("click", this.node, e);
33979         }else{
33980             e.stopEvent();
33981         }
33982     },
33983
33984     onDblClick : function(e){
33985         e.preventDefault();
33986         if(this.disabled){
33987             return;
33988         }
33989         if(this.checkbox){
33990             this.toggleCheck();
33991         }
33992         if(!this.animating && this.node.hasChildNodes()){
33993             this.node.toggle();
33994         }
33995         this.fireEvent("dblclick", this.node, e);
33996     },
33997
33998     onCheckChange : function(){
33999         var checked = this.checkbox.checked;
34000         this.node.attributes.checked = checked;
34001         this.fireEvent('checkchange', this.node, checked);
34002     },
34003
34004     ecClick : function(e){
34005         if(!this.animating && this.node.hasChildNodes()){
34006             this.node.toggle();
34007         }
34008     },
34009
34010     startDrop : function(){
34011         this.dropping = true;
34012     },
34013
34014     // delayed drop so the click event doesn't get fired on a drop
34015     endDrop : function(){
34016        setTimeout(function(){
34017            this.dropping = false;
34018        }.createDelegate(this), 50);
34019     },
34020
34021     expand : function(){
34022         this.updateExpandIcon();
34023         this.ctNode.style.display = "";
34024     },
34025
34026     focus : function(){
34027         if(!this.node.preventHScroll){
34028             try{this.anchor.focus();
34029             }catch(e){}
34030         }else if(!Roo.isIE){
34031             try{
34032                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34033                 var l = noscroll.scrollLeft;
34034                 this.anchor.focus();
34035                 noscroll.scrollLeft = l;
34036             }catch(e){}
34037         }
34038     },
34039
34040     toggleCheck : function(value){
34041         var cb = this.checkbox;
34042         if(cb){
34043             cb.checked = (value === undefined ? !cb.checked : value);
34044         }
34045     },
34046
34047     blur : function(){
34048         try{
34049             this.anchor.blur();
34050         }catch(e){}
34051     },
34052
34053     animExpand : function(callback){
34054         var ct = Roo.get(this.ctNode);
34055         ct.stopFx();
34056         if(!this.node.hasChildNodes()){
34057             this.updateExpandIcon();
34058             this.ctNode.style.display = "";
34059             Roo.callback(callback);
34060             return;
34061         }
34062         this.animating = true;
34063         this.updateExpandIcon();
34064
34065         ct.slideIn('t', {
34066            callback : function(){
34067                this.animating = false;
34068                Roo.callback(callback);
34069             },
34070             scope: this,
34071             duration: this.node.ownerTree.duration || .25
34072         });
34073     },
34074
34075     highlight : function(){
34076         var tree = this.node.getOwnerTree();
34077         Roo.fly(this.wrap).highlight(
34078             tree.hlColor || "C3DAF9",
34079             {endColor: tree.hlBaseColor}
34080         );
34081     },
34082
34083     collapse : function(){
34084         this.updateExpandIcon();
34085         this.ctNode.style.display = "none";
34086     },
34087
34088     animCollapse : function(callback){
34089         var ct = Roo.get(this.ctNode);
34090         ct.enableDisplayMode('block');
34091         ct.stopFx();
34092
34093         this.animating = true;
34094         this.updateExpandIcon();
34095
34096         ct.slideOut('t', {
34097             callback : function(){
34098                this.animating = false;
34099                Roo.callback(callback);
34100             },
34101             scope: this,
34102             duration: this.node.ownerTree.duration || .25
34103         });
34104     },
34105
34106     getContainer : function(){
34107         return this.ctNode;
34108     },
34109
34110     getEl : function(){
34111         return this.wrap;
34112     },
34113
34114     appendDDGhost : function(ghostNode){
34115         ghostNode.appendChild(this.elNode.cloneNode(true));
34116     },
34117
34118     getDDRepairXY : function(){
34119         return Roo.lib.Dom.getXY(this.iconNode);
34120     },
34121
34122     onRender : function(){
34123         this.render();
34124     },
34125
34126     render : function(bulkRender){
34127         var n = this.node, a = n.attributes;
34128         var targetNode = n.parentNode ?
34129               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34130
34131         if(!this.rendered){
34132             this.rendered = true;
34133
34134             this.renderElements(n, a, targetNode, bulkRender);
34135
34136             if(a.qtip){
34137                if(this.textNode.setAttributeNS){
34138                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34139                    if(a.qtipTitle){
34140                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34141                    }
34142                }else{
34143                    this.textNode.setAttribute("ext:qtip", a.qtip);
34144                    if(a.qtipTitle){
34145                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34146                    }
34147                }
34148             }else if(a.qtipCfg){
34149                 a.qtipCfg.target = Roo.id(this.textNode);
34150                 Roo.QuickTips.register(a.qtipCfg);
34151             }
34152             this.initEvents();
34153             if(!this.node.expanded){
34154                 this.updateExpandIcon();
34155             }
34156         }else{
34157             if(bulkRender === true) {
34158                 targetNode.appendChild(this.wrap);
34159             }
34160         }
34161     },
34162
34163     renderElements : function(n, a, targetNode, bulkRender)
34164     {
34165         // add some indent caching, this helps performance when rendering a large tree
34166         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34167         var t = n.getOwnerTree();
34168         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34169         if (typeof(n.attributes.html) != 'undefined') {
34170             txt = n.attributes.html;
34171         }
34172         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34173         var cb = typeof a.checked == 'boolean';
34174         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34175         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34176             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34177             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34178             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34179             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34180             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34181              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34182                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34183             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34184             "</li>"];
34185
34186         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34187             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34188                                 n.nextSibling.ui.getEl(), buf.join(""));
34189         }else{
34190             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34191         }
34192
34193         this.elNode = this.wrap.childNodes[0];
34194         this.ctNode = this.wrap.childNodes[1];
34195         var cs = this.elNode.childNodes;
34196         this.indentNode = cs[0];
34197         this.ecNode = cs[1];
34198         this.iconNode = cs[2];
34199         var index = 3;
34200         if(cb){
34201             this.checkbox = cs[3];
34202             index++;
34203         }
34204         this.anchor = cs[index];
34205         this.textNode = cs[index].firstChild;
34206     },
34207
34208     getAnchor : function(){
34209         return this.anchor;
34210     },
34211
34212     getTextEl : function(){
34213         return this.textNode;
34214     },
34215
34216     getIconEl : function(){
34217         return this.iconNode;
34218     },
34219
34220     isChecked : function(){
34221         return this.checkbox ? this.checkbox.checked : false;
34222     },
34223
34224     updateExpandIcon : function(){
34225         if(this.rendered){
34226             var n = this.node, c1, c2;
34227             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34228             var hasChild = n.hasChildNodes();
34229             if(hasChild){
34230                 if(n.expanded){
34231                     cls += "-minus";
34232                     c1 = "x-tree-node-collapsed";
34233                     c2 = "x-tree-node-expanded";
34234                 }else{
34235                     cls += "-plus";
34236                     c1 = "x-tree-node-expanded";
34237                     c2 = "x-tree-node-collapsed";
34238                 }
34239                 if(this.wasLeaf){
34240                     this.removeClass("x-tree-node-leaf");
34241                     this.wasLeaf = false;
34242                 }
34243                 if(this.c1 != c1 || this.c2 != c2){
34244                     Roo.fly(this.elNode).replaceClass(c1, c2);
34245                     this.c1 = c1; this.c2 = c2;
34246                 }
34247             }else{
34248                 // this changes non-leafs into leafs if they have no children.
34249                 // it's not very rational behaviour..
34250                 
34251                 if(!this.wasLeaf && this.node.leaf){
34252                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34253                     delete this.c1;
34254                     delete this.c2;
34255                     this.wasLeaf = true;
34256                 }
34257             }
34258             var ecc = "x-tree-ec-icon "+cls;
34259             if(this.ecc != ecc){
34260                 this.ecNode.className = ecc;
34261                 this.ecc = ecc;
34262             }
34263         }
34264     },
34265
34266     getChildIndent : function(){
34267         if(!this.childIndent){
34268             var buf = [];
34269             var p = this.node;
34270             while(p){
34271                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34272                     if(!p.isLast()) {
34273                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34274                     } else {
34275                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34276                     }
34277                 }
34278                 p = p.parentNode;
34279             }
34280             this.childIndent = buf.join("");
34281         }
34282         return this.childIndent;
34283     },
34284
34285     renderIndent : function(){
34286         if(this.rendered){
34287             var indent = "";
34288             var p = this.node.parentNode;
34289             if(p){
34290                 indent = p.ui.getChildIndent();
34291             }
34292             if(this.indentMarkup != indent){ // don't rerender if not required
34293                 this.indentNode.innerHTML = indent;
34294                 this.indentMarkup = indent;
34295             }
34296             this.updateExpandIcon();
34297         }
34298     }
34299 };
34300
34301 Roo.tree.RootTreeNodeUI = function(){
34302     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34303 };
34304 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34305     render : function(){
34306         if(!this.rendered){
34307             var targetNode = this.node.ownerTree.innerCt.dom;
34308             this.node.expanded = true;
34309             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34310             this.wrap = this.ctNode = targetNode.firstChild;
34311         }
34312     },
34313     collapse : function(){
34314     },
34315     expand : function(){
34316     }
34317 });/*
34318  * Based on:
34319  * Ext JS Library 1.1.1
34320  * Copyright(c) 2006-2007, Ext JS, LLC.
34321  *
34322  * Originally Released Under LGPL - original licence link has changed is not relivant.
34323  *
34324  * Fork - LGPL
34325  * <script type="text/javascript">
34326  */
34327 /**
34328  * @class Roo.tree.TreeLoader
34329  * @extends Roo.util.Observable
34330  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34331  * nodes from a specified URL. The response must be a javascript Array definition
34332  * who's elements are node definition objects. eg:
34333  * <pre><code>
34334 {  success : true,
34335    data :      [
34336    
34337     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34338     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34339     ]
34340 }
34341
34342
34343 </code></pre>
34344  * <br><br>
34345  * The old style respose with just an array is still supported, but not recommended.
34346  * <br><br>
34347  *
34348  * A server request is sent, and child nodes are loaded only when a node is expanded.
34349  * The loading node's id is passed to the server under the parameter name "node" to
34350  * enable the server to produce the correct child nodes.
34351  * <br><br>
34352  * To pass extra parameters, an event handler may be attached to the "beforeload"
34353  * event, and the parameters specified in the TreeLoader's baseParams property:
34354  * <pre><code>
34355     myTreeLoader.on("beforeload", function(treeLoader, node) {
34356         this.baseParams.category = node.attributes.category;
34357     }, this);
34358 </code></pre><
34359  * This would pass an HTTP parameter called "category" to the server containing
34360  * the value of the Node's "category" attribute.
34361  * @constructor
34362  * Creates a new Treeloader.
34363  * @param {Object} config A config object containing config properties.
34364  */
34365 Roo.tree.TreeLoader = function(config){
34366     this.baseParams = {};
34367     this.requestMethod = "POST";
34368     Roo.apply(this, config);
34369
34370     this.addEvents({
34371     
34372         /**
34373          * @event beforeload
34374          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34375          * @param {Object} This TreeLoader object.
34376          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34377          * @param {Object} callback The callback function specified in the {@link #load} call.
34378          */
34379         beforeload : true,
34380         /**
34381          * @event load
34382          * Fires when the node has been successfuly loaded.
34383          * @param {Object} This TreeLoader object.
34384          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34385          * @param {Object} response The response object containing the data from the server.
34386          */
34387         load : true,
34388         /**
34389          * @event loadexception
34390          * Fires if the network request failed.
34391          * @param {Object} This TreeLoader object.
34392          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34393          * @param {Object} response The response object containing the data from the server.
34394          */
34395         loadexception : true,
34396         /**
34397          * @event create
34398          * Fires before a node is created, enabling you to return custom Node types 
34399          * @param {Object} This TreeLoader object.
34400          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34401          */
34402         create : true
34403     });
34404
34405     Roo.tree.TreeLoader.superclass.constructor.call(this);
34406 };
34407
34408 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34409     /**
34410     * @cfg {String} dataUrl The URL from which to request a Json string which
34411     * specifies an array of node definition object representing the child nodes
34412     * to be loaded.
34413     */
34414     /**
34415     * @cfg {String} requestMethod either GET or POST
34416     * defaults to POST (due to BC)
34417     * to be loaded.
34418     */
34419     /**
34420     * @cfg {Object} baseParams (optional) An object containing properties which
34421     * specify HTTP parameters to be passed to each request for child nodes.
34422     */
34423     /**
34424     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34425     * created by this loader. If the attributes sent by the server have an attribute in this object,
34426     * they take priority.
34427     */
34428     /**
34429     * @cfg {Object} uiProviders (optional) An object containing properties which
34430     * 
34431     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34432     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34433     * <i>uiProvider</i> attribute of a returned child node is a string rather
34434     * than a reference to a TreeNodeUI implementation, this that string value
34435     * is used as a property name in the uiProviders object. You can define the provider named
34436     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34437     */
34438     uiProviders : {},
34439
34440     /**
34441     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34442     * child nodes before loading.
34443     */
34444     clearOnLoad : true,
34445
34446     /**
34447     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34448     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34449     * Grid query { data : [ .....] }
34450     */
34451     
34452     root : false,
34453      /**
34454     * @cfg {String} queryParam (optional) 
34455     * Name of the query as it will be passed on the querystring (defaults to 'node')
34456     * eg. the request will be ?node=[id]
34457     */
34458     
34459     
34460     queryParam: false,
34461     
34462     /**
34463      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34464      * This is called automatically when a node is expanded, but may be used to reload
34465      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34466      * @param {Roo.tree.TreeNode} node
34467      * @param {Function} callback
34468      */
34469     load : function(node, callback){
34470         if(this.clearOnLoad){
34471             while(node.firstChild){
34472                 node.removeChild(node.firstChild);
34473             }
34474         }
34475         if(node.attributes.children){ // preloaded json children
34476             var cs = node.attributes.children;
34477             for(var i = 0, len = cs.length; i < len; i++){
34478                 node.appendChild(this.createNode(cs[i]));
34479             }
34480             if(typeof callback == "function"){
34481                 callback();
34482             }
34483         }else if(this.dataUrl){
34484             this.requestData(node, callback);
34485         }
34486     },
34487
34488     getParams: function(node){
34489         var buf = [], bp = this.baseParams;
34490         for(var key in bp){
34491             if(typeof bp[key] != "function"){
34492                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34493             }
34494         }
34495         var n = this.queryParam === false ? 'node' : this.queryParam;
34496         buf.push(n + "=", encodeURIComponent(node.id));
34497         return buf.join("");
34498     },
34499
34500     requestData : function(node, callback){
34501         if(this.fireEvent("beforeload", this, node, callback) !== false){
34502             this.transId = Roo.Ajax.request({
34503                 method:this.requestMethod,
34504                 url: this.dataUrl||this.url,
34505                 success: this.handleResponse,
34506                 failure: this.handleFailure,
34507                 scope: this,
34508                 argument: {callback: callback, node: node},
34509                 params: this.getParams(node)
34510             });
34511         }else{
34512             // if the load is cancelled, make sure we notify
34513             // the node that we are done
34514             if(typeof callback == "function"){
34515                 callback();
34516             }
34517         }
34518     },
34519
34520     isLoading : function(){
34521         return this.transId ? true : false;
34522     },
34523
34524     abort : function(){
34525         if(this.isLoading()){
34526             Roo.Ajax.abort(this.transId);
34527         }
34528     },
34529
34530     // private
34531     createNode : function(attr)
34532     {
34533         // apply baseAttrs, nice idea Corey!
34534         if(this.baseAttrs){
34535             Roo.applyIf(attr, this.baseAttrs);
34536         }
34537         if(this.applyLoader !== false){
34538             attr.loader = this;
34539         }
34540         // uiProvider = depreciated..
34541         
34542         if(typeof(attr.uiProvider) == 'string'){
34543            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34544                 /**  eval:var:attr */ eval(attr.uiProvider);
34545         }
34546         if(typeof(this.uiProviders['default']) != 'undefined') {
34547             attr.uiProvider = this.uiProviders['default'];
34548         }
34549         
34550         this.fireEvent('create', this, attr);
34551         
34552         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34553         return(attr.leaf ?
34554                         new Roo.tree.TreeNode(attr) :
34555                         new Roo.tree.AsyncTreeNode(attr));
34556     },
34557
34558     processResponse : function(response, node, callback)
34559     {
34560         var json = response.responseText;
34561         try {
34562             
34563             var o = Roo.decode(json);
34564             
34565             if (this.root === false && typeof(o.success) != undefined) {
34566                 this.root = 'data'; // the default behaviour for list like data..
34567                 }
34568                 
34569             if (this.root !== false &&  !o.success) {
34570                 // it's a failure condition.
34571                 var a = response.argument;
34572                 this.fireEvent("loadexception", this, a.node, response);
34573                 Roo.log("Load failed - should have a handler really");
34574                 return;
34575             }
34576             
34577             
34578             
34579             if (this.root !== false) {
34580                  o = o[this.root];
34581             }
34582             
34583             for(var i = 0, len = o.length; i < len; i++){
34584                 var n = this.createNode(o[i]);
34585                 if(n){
34586                     node.appendChild(n);
34587                 }
34588             }
34589             if(typeof callback == "function"){
34590                 callback(this, node);
34591             }
34592         }catch(e){
34593             this.handleFailure(response);
34594         }
34595     },
34596
34597     handleResponse : function(response){
34598         this.transId = false;
34599         var a = response.argument;
34600         this.processResponse(response, a.node, a.callback);
34601         this.fireEvent("load", this, a.node, response);
34602     },
34603
34604     handleFailure : function(response)
34605     {
34606         // should handle failure better..
34607         this.transId = false;
34608         var a = response.argument;
34609         this.fireEvent("loadexception", this, a.node, response);
34610         if(typeof a.callback == "function"){
34611             a.callback(this, a.node);
34612         }
34613     }
34614 });/*
34615  * Based on:
34616  * Ext JS Library 1.1.1
34617  * Copyright(c) 2006-2007, Ext JS, LLC.
34618  *
34619  * Originally Released Under LGPL - original licence link has changed is not relivant.
34620  *
34621  * Fork - LGPL
34622  * <script type="text/javascript">
34623  */
34624
34625 /**
34626 * @class Roo.tree.TreeFilter
34627 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34628 * @param {TreePanel} tree
34629 * @param {Object} config (optional)
34630  */
34631 Roo.tree.TreeFilter = function(tree, config){
34632     this.tree = tree;
34633     this.filtered = {};
34634     Roo.apply(this, config);
34635 };
34636
34637 Roo.tree.TreeFilter.prototype = {
34638     clearBlank:false,
34639     reverse:false,
34640     autoClear:false,
34641     remove:false,
34642
34643      /**
34644      * Filter the data by a specific attribute.
34645      * @param {String/RegExp} value Either string that the attribute value
34646      * should start with or a RegExp to test against the attribute
34647      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34648      * @param {TreeNode} startNode (optional) The node to start the filter at.
34649      */
34650     filter : function(value, attr, startNode){
34651         attr = attr || "text";
34652         var f;
34653         if(typeof value == "string"){
34654             var vlen = value.length;
34655             // auto clear empty filter
34656             if(vlen == 0 && this.clearBlank){
34657                 this.clear();
34658                 return;
34659             }
34660             value = value.toLowerCase();
34661             f = function(n){
34662                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34663             };
34664         }else if(value.exec){ // regex?
34665             f = function(n){
34666                 return value.test(n.attributes[attr]);
34667             };
34668         }else{
34669             throw 'Illegal filter type, must be string or regex';
34670         }
34671         this.filterBy(f, null, startNode);
34672         },
34673
34674     /**
34675      * Filter by a function. The passed function will be called with each
34676      * node in the tree (or from the startNode). If the function returns true, the node is kept
34677      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34678      * @param {Function} fn The filter function
34679      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34680      */
34681     filterBy : function(fn, scope, startNode){
34682         startNode = startNode || this.tree.root;
34683         if(this.autoClear){
34684             this.clear();
34685         }
34686         var af = this.filtered, rv = this.reverse;
34687         var f = function(n){
34688             if(n == startNode){
34689                 return true;
34690             }
34691             if(af[n.id]){
34692                 return false;
34693             }
34694             var m = fn.call(scope || n, n);
34695             if(!m || rv){
34696                 af[n.id] = n;
34697                 n.ui.hide();
34698                 return false;
34699             }
34700             return true;
34701         };
34702         startNode.cascade(f);
34703         if(this.remove){
34704            for(var id in af){
34705                if(typeof id != "function"){
34706                    var n = af[id];
34707                    if(n && n.parentNode){
34708                        n.parentNode.removeChild(n);
34709                    }
34710                }
34711            }
34712         }
34713     },
34714
34715     /**
34716      * Clears the current filter. Note: with the "remove" option
34717      * set a filter cannot be cleared.
34718      */
34719     clear : function(){
34720         var t = this.tree;
34721         var af = this.filtered;
34722         for(var id in af){
34723             if(typeof id != "function"){
34724                 var n = af[id];
34725                 if(n){
34726                     n.ui.show();
34727                 }
34728             }
34729         }
34730         this.filtered = {};
34731     }
34732 };
34733 /*
34734  * Based on:
34735  * Ext JS Library 1.1.1
34736  * Copyright(c) 2006-2007, Ext JS, LLC.
34737  *
34738  * Originally Released Under LGPL - original licence link has changed is not relivant.
34739  *
34740  * Fork - LGPL
34741  * <script type="text/javascript">
34742  */
34743  
34744
34745 /**
34746  * @class Roo.tree.TreeSorter
34747  * Provides sorting of nodes in a TreePanel
34748  * 
34749  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34750  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34751  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34752  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34753  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34754  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34755  * @constructor
34756  * @param {TreePanel} tree
34757  * @param {Object} config
34758  */
34759 Roo.tree.TreeSorter = function(tree, config){
34760     Roo.apply(this, config);
34761     tree.on("beforechildrenrendered", this.doSort, this);
34762     tree.on("append", this.updateSort, this);
34763     tree.on("insert", this.updateSort, this);
34764     
34765     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34766     var p = this.property || "text";
34767     var sortType = this.sortType;
34768     var fs = this.folderSort;
34769     var cs = this.caseSensitive === true;
34770     var leafAttr = this.leafAttr || 'leaf';
34771
34772     this.sortFn = function(n1, n2){
34773         if(fs){
34774             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34775                 return 1;
34776             }
34777             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34778                 return -1;
34779             }
34780         }
34781         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34782         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34783         if(v1 < v2){
34784                         return dsc ? +1 : -1;
34785                 }else if(v1 > v2){
34786                         return dsc ? -1 : +1;
34787         }else{
34788                 return 0;
34789         }
34790     };
34791 };
34792
34793 Roo.tree.TreeSorter.prototype = {
34794     doSort : function(node){
34795         node.sort(this.sortFn);
34796     },
34797     
34798     compareNodes : function(n1, n2){
34799         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34800     },
34801     
34802     updateSort : function(tree, node){
34803         if(node.childrenRendered){
34804             this.doSort.defer(1, this, [node]);
34805         }
34806     }
34807 };/*
34808  * Based on:
34809  * Ext JS Library 1.1.1
34810  * Copyright(c) 2006-2007, Ext JS, LLC.
34811  *
34812  * Originally Released Under LGPL - original licence link has changed is not relivant.
34813  *
34814  * Fork - LGPL
34815  * <script type="text/javascript">
34816  */
34817
34818 if(Roo.dd.DropZone){
34819     
34820 Roo.tree.TreeDropZone = function(tree, config){
34821     this.allowParentInsert = false;
34822     this.allowContainerDrop = false;
34823     this.appendOnly = false;
34824     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34825     this.tree = tree;
34826     this.lastInsertClass = "x-tree-no-status";
34827     this.dragOverData = {};
34828 };
34829
34830 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34831     ddGroup : "TreeDD",
34832     scroll:  true,
34833     
34834     expandDelay : 1000,
34835     
34836     expandNode : function(node){
34837         if(node.hasChildNodes() && !node.isExpanded()){
34838             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34839         }
34840     },
34841     
34842     queueExpand : function(node){
34843         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34844     },
34845     
34846     cancelExpand : function(){
34847         if(this.expandProcId){
34848             clearTimeout(this.expandProcId);
34849             this.expandProcId = false;
34850         }
34851     },
34852     
34853     isValidDropPoint : function(n, pt, dd, e, data){
34854         if(!n || !data){ return false; }
34855         var targetNode = n.node;
34856         var dropNode = data.node;
34857         // default drop rules
34858         if(!(targetNode && targetNode.isTarget && pt)){
34859             return false;
34860         }
34861         if(pt == "append" && targetNode.allowChildren === false){
34862             return false;
34863         }
34864         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34865             return false;
34866         }
34867         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34868             return false;
34869         }
34870         // reuse the object
34871         var overEvent = this.dragOverData;
34872         overEvent.tree = this.tree;
34873         overEvent.target = targetNode;
34874         overEvent.data = data;
34875         overEvent.point = pt;
34876         overEvent.source = dd;
34877         overEvent.rawEvent = e;
34878         overEvent.dropNode = dropNode;
34879         overEvent.cancel = false;  
34880         var result = this.tree.fireEvent("nodedragover", overEvent);
34881         return overEvent.cancel === false && result !== false;
34882     },
34883     
34884     getDropPoint : function(e, n, dd)
34885     {
34886         var tn = n.node;
34887         if(tn.isRoot){
34888             return tn.allowChildren !== false ? "append" : false; // always append for root
34889         }
34890         var dragEl = n.ddel;
34891         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34892         var y = Roo.lib.Event.getPageY(e);
34893         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34894         
34895         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34896         var noAppend = tn.allowChildren === false;
34897         if(this.appendOnly || tn.parentNode.allowChildren === false){
34898             return noAppend ? false : "append";
34899         }
34900         var noBelow = false;
34901         if(!this.allowParentInsert){
34902             noBelow = tn.hasChildNodes() && tn.isExpanded();
34903         }
34904         var q = (b - t) / (noAppend ? 2 : 3);
34905         if(y >= t && y < (t + q)){
34906             return "above";
34907         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34908             return "below";
34909         }else{
34910             return "append";
34911         }
34912     },
34913     
34914     onNodeEnter : function(n, dd, e, data)
34915     {
34916         this.cancelExpand();
34917     },
34918     
34919     onNodeOver : function(n, dd, e, data)
34920     {
34921        
34922         var pt = this.getDropPoint(e, n, dd);
34923         var node = n.node;
34924         
34925         // auto node expand check
34926         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34927             this.queueExpand(node);
34928         }else if(pt != "append"){
34929             this.cancelExpand();
34930         }
34931         
34932         // set the insert point style on the target node
34933         var returnCls = this.dropNotAllowed;
34934         if(this.isValidDropPoint(n, pt, dd, e, data)){
34935            if(pt){
34936                var el = n.ddel;
34937                var cls;
34938                if(pt == "above"){
34939                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34940                    cls = "x-tree-drag-insert-above";
34941                }else if(pt == "below"){
34942                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34943                    cls = "x-tree-drag-insert-below";
34944                }else{
34945                    returnCls = "x-tree-drop-ok-append";
34946                    cls = "x-tree-drag-append";
34947                }
34948                if(this.lastInsertClass != cls){
34949                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34950                    this.lastInsertClass = cls;
34951                }
34952            }
34953        }
34954        return returnCls;
34955     },
34956     
34957     onNodeOut : function(n, dd, e, data){
34958         
34959         this.cancelExpand();
34960         this.removeDropIndicators(n);
34961     },
34962     
34963     onNodeDrop : function(n, dd, e, data){
34964         var point = this.getDropPoint(e, n, dd);
34965         var targetNode = n.node;
34966         targetNode.ui.startDrop();
34967         if(!this.isValidDropPoint(n, point, dd, e, data)){
34968             targetNode.ui.endDrop();
34969             return false;
34970         }
34971         // first try to find the drop node
34972         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34973         var dropEvent = {
34974             tree : this.tree,
34975             target: targetNode,
34976             data: data,
34977             point: point,
34978             source: dd,
34979             rawEvent: e,
34980             dropNode: dropNode,
34981             cancel: !dropNode   
34982         };
34983         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34984         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34985             targetNode.ui.endDrop();
34986             return false;
34987         }
34988         // allow target changing
34989         targetNode = dropEvent.target;
34990         if(point == "append" && !targetNode.isExpanded()){
34991             targetNode.expand(false, null, function(){
34992                 this.completeDrop(dropEvent);
34993             }.createDelegate(this));
34994         }else{
34995             this.completeDrop(dropEvent);
34996         }
34997         return true;
34998     },
34999     
35000     completeDrop : function(de){
35001         var ns = de.dropNode, p = de.point, t = de.target;
35002         if(!(ns instanceof Array)){
35003             ns = [ns];
35004         }
35005         var n;
35006         for(var i = 0, len = ns.length; i < len; i++){
35007             n = ns[i];
35008             if(p == "above"){
35009                 t.parentNode.insertBefore(n, t);
35010             }else if(p == "below"){
35011                 t.parentNode.insertBefore(n, t.nextSibling);
35012             }else{
35013                 t.appendChild(n);
35014             }
35015         }
35016         n.ui.focus();
35017         if(this.tree.hlDrop){
35018             n.ui.highlight();
35019         }
35020         t.ui.endDrop();
35021         this.tree.fireEvent("nodedrop", de);
35022     },
35023     
35024     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35025         if(this.tree.hlDrop){
35026             dropNode.ui.focus();
35027             dropNode.ui.highlight();
35028         }
35029         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35030     },
35031     
35032     getTree : function(){
35033         return this.tree;
35034     },
35035     
35036     removeDropIndicators : function(n){
35037         if(n && n.ddel){
35038             var el = n.ddel;
35039             Roo.fly(el).removeClass([
35040                     "x-tree-drag-insert-above",
35041                     "x-tree-drag-insert-below",
35042                     "x-tree-drag-append"]);
35043             this.lastInsertClass = "_noclass";
35044         }
35045     },
35046     
35047     beforeDragDrop : function(target, e, id){
35048         this.cancelExpand();
35049         return true;
35050     },
35051     
35052     afterRepair : function(data){
35053         if(data && Roo.enableFx){
35054             data.node.ui.highlight();
35055         }
35056         this.hideProxy();
35057     } 
35058     
35059 });
35060
35061 }
35062 /*
35063  * Based on:
35064  * Ext JS Library 1.1.1
35065  * Copyright(c) 2006-2007, Ext JS, LLC.
35066  *
35067  * Originally Released Under LGPL - original licence link has changed is not relivant.
35068  *
35069  * Fork - LGPL
35070  * <script type="text/javascript">
35071  */
35072  
35073
35074 if(Roo.dd.DragZone){
35075 Roo.tree.TreeDragZone = function(tree, config){
35076     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35077     this.tree = tree;
35078 };
35079
35080 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35081     ddGroup : "TreeDD",
35082    
35083     onBeforeDrag : function(data, e){
35084         var n = data.node;
35085         return n && n.draggable && !n.disabled;
35086     },
35087      
35088     
35089     onInitDrag : function(e){
35090         var data = this.dragData;
35091         this.tree.getSelectionModel().select(data.node);
35092         this.proxy.update("");
35093         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35094         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35095     },
35096     
35097     getRepairXY : function(e, data){
35098         return data.node.ui.getDDRepairXY();
35099     },
35100     
35101     onEndDrag : function(data, e){
35102         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35103         
35104         
35105     },
35106     
35107     onValidDrop : function(dd, e, id){
35108         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35109         this.hideProxy();
35110     },
35111     
35112     beforeInvalidDrop : function(e, id){
35113         // this scrolls the original position back into view
35114         var sm = this.tree.getSelectionModel();
35115         sm.clearSelections();
35116         sm.select(this.dragData.node);
35117     }
35118 });
35119 }/*
35120  * Based on:
35121  * Ext JS Library 1.1.1
35122  * Copyright(c) 2006-2007, Ext JS, LLC.
35123  *
35124  * Originally Released Under LGPL - original licence link has changed is not relivant.
35125  *
35126  * Fork - LGPL
35127  * <script type="text/javascript">
35128  */
35129 /**
35130  * @class Roo.tree.TreeEditor
35131  * @extends Roo.Editor
35132  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35133  * as the editor field.
35134  * @constructor
35135  * @param {Object} config (used to be the tree panel.)
35136  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35137  * 
35138  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35139  * @cfg {Roo.form.TextField|Object} field The field configuration
35140  *
35141  * 
35142  */
35143 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35144     var tree = config;
35145     var field;
35146     if (oldconfig) { // old style..
35147         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35148     } else {
35149         // new style..
35150         tree = config.tree;
35151         config.field = config.field  || {};
35152         config.field.xtype = 'TextField';
35153         field = Roo.factory(config.field, Roo.form);
35154     }
35155     config = config || {};
35156     
35157     
35158     this.addEvents({
35159         /**
35160          * @event beforenodeedit
35161          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35162          * false from the handler of this event.
35163          * @param {Editor} this
35164          * @param {Roo.tree.Node} node 
35165          */
35166         "beforenodeedit" : true
35167     });
35168     
35169     //Roo.log(config);
35170     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35171
35172     this.tree = tree;
35173
35174     tree.on('beforeclick', this.beforeNodeClick, this);
35175     tree.getTreeEl().on('mousedown', this.hide, this);
35176     this.on('complete', this.updateNode, this);
35177     this.on('beforestartedit', this.fitToTree, this);
35178     this.on('startedit', this.bindScroll, this, {delay:10});
35179     this.on('specialkey', this.onSpecialKey, this);
35180 };
35181
35182 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35183     /**
35184      * @cfg {String} alignment
35185      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35186      */
35187     alignment: "l-l",
35188     // inherit
35189     autoSize: false,
35190     /**
35191      * @cfg {Boolean} hideEl
35192      * True to hide the bound element while the editor is displayed (defaults to false)
35193      */
35194     hideEl : false,
35195     /**
35196      * @cfg {String} cls
35197      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35198      */
35199     cls: "x-small-editor x-tree-editor",
35200     /**
35201      * @cfg {Boolean} shim
35202      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35203      */
35204     shim:false,
35205     // inherit
35206     shadow:"frame",
35207     /**
35208      * @cfg {Number} maxWidth
35209      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35210      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35211      * scroll and client offsets into account prior to each edit.
35212      */
35213     maxWidth: 250,
35214
35215     editDelay : 350,
35216
35217     // private
35218     fitToTree : function(ed, el){
35219         var td = this.tree.getTreeEl().dom, nd = el.dom;
35220         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35221             td.scrollLeft = nd.offsetLeft;
35222         }
35223         var w = Math.min(
35224                 this.maxWidth,
35225                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35226         this.setSize(w, '');
35227         
35228         return this.fireEvent('beforenodeedit', this, this.editNode);
35229         
35230     },
35231
35232     // private
35233     triggerEdit : function(node){
35234         this.completeEdit();
35235         this.editNode = node;
35236         this.startEdit(node.ui.textNode, node.text);
35237     },
35238
35239     // private
35240     bindScroll : function(){
35241         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35242     },
35243
35244     // private
35245     beforeNodeClick : function(node, e){
35246         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35247         this.lastClick = new Date();
35248         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35249             e.stopEvent();
35250             this.triggerEdit(node);
35251             return false;
35252         }
35253         return true;
35254     },
35255
35256     // private
35257     updateNode : function(ed, value){
35258         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35259         this.editNode.setText(value);
35260     },
35261
35262     // private
35263     onHide : function(){
35264         Roo.tree.TreeEditor.superclass.onHide.call(this);
35265         if(this.editNode){
35266             this.editNode.ui.focus();
35267         }
35268     },
35269
35270     // private
35271     onSpecialKey : function(field, e){
35272         var k = e.getKey();
35273         if(k == e.ESC){
35274             e.stopEvent();
35275             this.cancelEdit();
35276         }else if(k == e.ENTER && !e.hasModifier()){
35277             e.stopEvent();
35278             this.completeEdit();
35279         }
35280     }
35281 });//<Script type="text/javascript">
35282 /*
35283  * Based on:
35284  * Ext JS Library 1.1.1
35285  * Copyright(c) 2006-2007, Ext JS, LLC.
35286  *
35287  * Originally Released Under LGPL - original licence link has changed is not relivant.
35288  *
35289  * Fork - LGPL
35290  * <script type="text/javascript">
35291  */
35292  
35293 /**
35294  * Not documented??? - probably should be...
35295  */
35296
35297 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35298     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35299     
35300     renderElements : function(n, a, targetNode, bulkRender){
35301         //consel.log("renderElements?");
35302         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35303
35304         var t = n.getOwnerTree();
35305         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35306         
35307         var cols = t.columns;
35308         var bw = t.borderWidth;
35309         var c = cols[0];
35310         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35311          var cb = typeof a.checked == "boolean";
35312         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35313         var colcls = 'x-t-' + tid + '-c0';
35314         var buf = [
35315             '<li class="x-tree-node">',
35316             
35317                 
35318                 '<div class="x-tree-node-el ', a.cls,'">',
35319                     // extran...
35320                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35321                 
35322                 
35323                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35324                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35325                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35326                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35327                            (a.iconCls ? ' '+a.iconCls : ''),
35328                            '" unselectable="on" />',
35329                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35330                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35331                              
35332                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35333                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35334                             '<span unselectable="on" qtip="' + tx + '">',
35335                              tx,
35336                              '</span></a>' ,
35337                     '</div>',
35338                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35339                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35340                  ];
35341         for(var i = 1, len = cols.length; i < len; i++){
35342             c = cols[i];
35343             colcls = 'x-t-' + tid + '-c' +i;
35344             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35345             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35346                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35347                       "</div>");
35348          }
35349          
35350          buf.push(
35351             '</a>',
35352             '<div class="x-clear"></div></div>',
35353             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35354             "</li>");
35355         
35356         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35357             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35358                                 n.nextSibling.ui.getEl(), buf.join(""));
35359         }else{
35360             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35361         }
35362         var el = this.wrap.firstChild;
35363         this.elRow = el;
35364         this.elNode = el.firstChild;
35365         this.ranchor = el.childNodes[1];
35366         this.ctNode = this.wrap.childNodes[1];
35367         var cs = el.firstChild.childNodes;
35368         this.indentNode = cs[0];
35369         this.ecNode = cs[1];
35370         this.iconNode = cs[2];
35371         var index = 3;
35372         if(cb){
35373             this.checkbox = cs[3];
35374             index++;
35375         }
35376         this.anchor = cs[index];
35377         
35378         this.textNode = cs[index].firstChild;
35379         
35380         //el.on("click", this.onClick, this);
35381         //el.on("dblclick", this.onDblClick, this);
35382         
35383         
35384        // console.log(this);
35385     },
35386     initEvents : function(){
35387         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35388         
35389             
35390         var a = this.ranchor;
35391
35392         var el = Roo.get(a);
35393
35394         if(Roo.isOpera){ // opera render bug ignores the CSS
35395             el.setStyle("text-decoration", "none");
35396         }
35397
35398         el.on("click", this.onClick, this);
35399         el.on("dblclick", this.onDblClick, this);
35400         el.on("contextmenu", this.onContextMenu, this);
35401         
35402     },
35403     
35404     /*onSelectedChange : function(state){
35405         if(state){
35406             this.focus();
35407             this.addClass("x-tree-selected");
35408         }else{
35409             //this.blur();
35410             this.removeClass("x-tree-selected");
35411         }
35412     },*/
35413     addClass : function(cls){
35414         if(this.elRow){
35415             Roo.fly(this.elRow).addClass(cls);
35416         }
35417         
35418     },
35419     
35420     
35421     removeClass : function(cls){
35422         if(this.elRow){
35423             Roo.fly(this.elRow).removeClass(cls);
35424         }
35425     }
35426
35427     
35428     
35429 });//<Script type="text/javascript">
35430
35431 /*
35432  * Based on:
35433  * Ext JS Library 1.1.1
35434  * Copyright(c) 2006-2007, Ext JS, LLC.
35435  *
35436  * Originally Released Under LGPL - original licence link has changed is not relivant.
35437  *
35438  * Fork - LGPL
35439  * <script type="text/javascript">
35440  */
35441  
35442
35443 /**
35444  * @class Roo.tree.ColumnTree
35445  * @extends Roo.data.TreePanel
35446  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35447  * @cfg {int} borderWidth  compined right/left border allowance
35448  * @constructor
35449  * @param {String/HTMLElement/Element} el The container element
35450  * @param {Object} config
35451  */
35452 Roo.tree.ColumnTree =  function(el, config)
35453 {
35454    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35455    this.addEvents({
35456         /**
35457         * @event resize
35458         * Fire this event on a container when it resizes
35459         * @param {int} w Width
35460         * @param {int} h Height
35461         */
35462        "resize" : true
35463     });
35464     this.on('resize', this.onResize, this);
35465 };
35466
35467 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35468     //lines:false,
35469     
35470     
35471     borderWidth: Roo.isBorderBox ? 0 : 2, 
35472     headEls : false,
35473     
35474     render : function(){
35475         // add the header.....
35476        
35477         Roo.tree.ColumnTree.superclass.render.apply(this);
35478         
35479         this.el.addClass('x-column-tree');
35480         
35481         this.headers = this.el.createChild(
35482             {cls:'x-tree-headers'},this.innerCt.dom);
35483    
35484         var cols = this.columns, c;
35485         var totalWidth = 0;
35486         this.headEls = [];
35487         var  len = cols.length;
35488         for(var i = 0; i < len; i++){
35489              c = cols[i];
35490              totalWidth += c.width;
35491             this.headEls.push(this.headers.createChild({
35492                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35493                  cn: {
35494                      cls:'x-tree-hd-text',
35495                      html: c.header
35496                  },
35497                  style:'width:'+(c.width-this.borderWidth)+'px;'
35498              }));
35499         }
35500         this.headers.createChild({cls:'x-clear'});
35501         // prevent floats from wrapping when clipped
35502         this.headers.setWidth(totalWidth);
35503         //this.innerCt.setWidth(totalWidth);
35504         this.innerCt.setStyle({ overflow: 'auto' });
35505         this.onResize(this.width, this.height);
35506              
35507         
35508     },
35509     onResize : function(w,h)
35510     {
35511         this.height = h;
35512         this.width = w;
35513         // resize cols..
35514         this.innerCt.setWidth(this.width);
35515         this.innerCt.setHeight(this.height-20);
35516         
35517         // headers...
35518         var cols = this.columns, c;
35519         var totalWidth = 0;
35520         var expEl = false;
35521         var len = cols.length;
35522         for(var i = 0; i < len; i++){
35523             c = cols[i];
35524             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35525                 // it's the expander..
35526                 expEl  = this.headEls[i];
35527                 continue;
35528             }
35529             totalWidth += c.width;
35530             
35531         }
35532         if (expEl) {
35533             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35534         }
35535         this.headers.setWidth(w-20);
35536
35537         
35538         
35539         
35540     }
35541 });
35542 /*
35543  * Based on:
35544  * Ext JS Library 1.1.1
35545  * Copyright(c) 2006-2007, Ext JS, LLC.
35546  *
35547  * Originally Released Under LGPL - original licence link has changed is not relivant.
35548  *
35549  * Fork - LGPL
35550  * <script type="text/javascript">
35551  */
35552  
35553 /**
35554  * @class Roo.menu.Menu
35555  * @extends Roo.util.Observable
35556  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35557  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35558  * @constructor
35559  * Creates a new Menu
35560  * @param {Object} config Configuration options
35561  */
35562 Roo.menu.Menu = function(config){
35563     Roo.apply(this, config);
35564     this.id = this.id || Roo.id();
35565     this.addEvents({
35566         /**
35567          * @event beforeshow
35568          * Fires before this menu is displayed
35569          * @param {Roo.menu.Menu} this
35570          */
35571         beforeshow : true,
35572         /**
35573          * @event beforehide
35574          * Fires before this menu is hidden
35575          * @param {Roo.menu.Menu} this
35576          */
35577         beforehide : true,
35578         /**
35579          * @event show
35580          * Fires after this menu is displayed
35581          * @param {Roo.menu.Menu} this
35582          */
35583         show : true,
35584         /**
35585          * @event hide
35586          * Fires after this menu is hidden
35587          * @param {Roo.menu.Menu} this
35588          */
35589         hide : true,
35590         /**
35591          * @event click
35592          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35593          * @param {Roo.menu.Menu} this
35594          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35595          * @param {Roo.EventObject} e
35596          */
35597         click : true,
35598         /**
35599          * @event mouseover
35600          * Fires when the mouse is hovering over this menu
35601          * @param {Roo.menu.Menu} this
35602          * @param {Roo.EventObject} e
35603          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35604          */
35605         mouseover : true,
35606         /**
35607          * @event mouseout
35608          * Fires when the mouse exits this menu
35609          * @param {Roo.menu.Menu} this
35610          * @param {Roo.EventObject} e
35611          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35612          */
35613         mouseout : true,
35614         /**
35615          * @event itemclick
35616          * Fires when a menu item contained in this menu is clicked
35617          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35618          * @param {Roo.EventObject} e
35619          */
35620         itemclick: true
35621     });
35622     if (this.registerMenu) {
35623         Roo.menu.MenuMgr.register(this);
35624     }
35625     
35626     var mis = this.items;
35627     this.items = new Roo.util.MixedCollection();
35628     if(mis){
35629         this.add.apply(this, mis);
35630     }
35631 };
35632
35633 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35634     /**
35635      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35636      */
35637     minWidth : 120,
35638     /**
35639      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35640      * for bottom-right shadow (defaults to "sides")
35641      */
35642     shadow : "sides",
35643     /**
35644      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35645      * this menu (defaults to "tl-tr?")
35646      */
35647     subMenuAlign : "tl-tr?",
35648     /**
35649      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35650      * relative to its element of origin (defaults to "tl-bl?")
35651      */
35652     defaultAlign : "tl-bl?",
35653     /**
35654      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35655      */
35656     allowOtherMenus : false,
35657     /**
35658      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35659      */
35660     registerMenu : true,
35661
35662     hidden:true,
35663
35664     // private
35665     render : function(){
35666         if(this.el){
35667             return;
35668         }
35669         var el = this.el = new Roo.Layer({
35670             cls: "x-menu",
35671             shadow:this.shadow,
35672             constrain: false,
35673             parentEl: this.parentEl || document.body,
35674             zindex:15000
35675         });
35676
35677         this.keyNav = new Roo.menu.MenuNav(this);
35678
35679         if(this.plain){
35680             el.addClass("x-menu-plain");
35681         }
35682         if(this.cls){
35683             el.addClass(this.cls);
35684         }
35685         // generic focus element
35686         this.focusEl = el.createChild({
35687             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35688         });
35689         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35690         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35691         
35692         ul.on("mouseover", this.onMouseOver, this);
35693         ul.on("mouseout", this.onMouseOut, this);
35694         this.items.each(function(item){
35695             if (item.hidden) {
35696                 return;
35697             }
35698             
35699             var li = document.createElement("li");
35700             li.className = "x-menu-list-item";
35701             ul.dom.appendChild(li);
35702             item.render(li, this);
35703         }, this);
35704         this.ul = ul;
35705         this.autoWidth();
35706     },
35707
35708     // private
35709     autoWidth : function(){
35710         var el = this.el, ul = this.ul;
35711         if(!el){
35712             return;
35713         }
35714         var w = this.width;
35715         if(w){
35716             el.setWidth(w);
35717         }else if(Roo.isIE){
35718             el.setWidth(this.minWidth);
35719             var t = el.dom.offsetWidth; // force recalc
35720             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35721         }
35722     },
35723
35724     // private
35725     delayAutoWidth : function(){
35726         if(this.rendered){
35727             if(!this.awTask){
35728                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35729             }
35730             this.awTask.delay(20);
35731         }
35732     },
35733
35734     // private
35735     findTargetItem : function(e){
35736         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35737         if(t && t.menuItemId){
35738             return this.items.get(t.menuItemId);
35739         }
35740     },
35741
35742     // private
35743     onClick : function(e){
35744         Roo.log("menu.onClick");
35745         var t = this.findTargetItem(e);
35746         if(!t){
35747             return;
35748         }
35749         Roo.log(e);
35750         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35751             if(t == this.activeItem && t.shouldDeactivate(e)){
35752                 this.activeItem.deactivate();
35753                 delete this.activeItem;
35754                 return;
35755             }
35756             if(t.canActivate){
35757                 this.setActiveItem(t, true);
35758             }
35759             return;
35760             
35761             
35762         }
35763         
35764         t.onClick(e);
35765         this.fireEvent("click", this, t, e);
35766     },
35767
35768     // private
35769     setActiveItem : function(item, autoExpand){
35770         if(item != this.activeItem){
35771             if(this.activeItem){
35772                 this.activeItem.deactivate();
35773             }
35774             this.activeItem = item;
35775             item.activate(autoExpand);
35776         }else if(autoExpand){
35777             item.expandMenu();
35778         }
35779     },
35780
35781     // private
35782     tryActivate : function(start, step){
35783         var items = this.items;
35784         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35785             var item = items.get(i);
35786             if(!item.disabled && item.canActivate){
35787                 this.setActiveItem(item, false);
35788                 return item;
35789             }
35790         }
35791         return false;
35792     },
35793
35794     // private
35795     onMouseOver : function(e){
35796         var t;
35797         if(t = this.findTargetItem(e)){
35798             if(t.canActivate && !t.disabled){
35799                 this.setActiveItem(t, true);
35800             }
35801         }
35802         this.fireEvent("mouseover", this, e, t);
35803     },
35804
35805     // private
35806     onMouseOut : function(e){
35807         var t;
35808         if(t = this.findTargetItem(e)){
35809             if(t == this.activeItem && t.shouldDeactivate(e)){
35810                 this.activeItem.deactivate();
35811                 delete this.activeItem;
35812             }
35813         }
35814         this.fireEvent("mouseout", this, e, t);
35815     },
35816
35817     /**
35818      * Read-only.  Returns true if the menu is currently displayed, else false.
35819      * @type Boolean
35820      */
35821     isVisible : function(){
35822         return this.el && !this.hidden;
35823     },
35824
35825     /**
35826      * Displays this menu relative to another element
35827      * @param {String/HTMLElement/Roo.Element} element The element to align to
35828      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35829      * the element (defaults to this.defaultAlign)
35830      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35831      */
35832     show : function(el, pos, parentMenu){
35833         this.parentMenu = parentMenu;
35834         if(!this.el){
35835             this.render();
35836         }
35837         this.fireEvent("beforeshow", this);
35838         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35839     },
35840
35841     /**
35842      * Displays this menu at a specific xy position
35843      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35844      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35845      */
35846     showAt : function(xy, parentMenu, /* private: */_e){
35847         this.parentMenu = parentMenu;
35848         if(!this.el){
35849             this.render();
35850         }
35851         if(_e !== false){
35852             this.fireEvent("beforeshow", this);
35853             xy = this.el.adjustForConstraints(xy);
35854         }
35855         this.el.setXY(xy);
35856         this.el.show();
35857         this.hidden = false;
35858         this.focus();
35859         this.fireEvent("show", this);
35860     },
35861
35862     focus : function(){
35863         if(!this.hidden){
35864             this.doFocus.defer(50, this);
35865         }
35866     },
35867
35868     doFocus : function(){
35869         if(!this.hidden){
35870             this.focusEl.focus();
35871         }
35872     },
35873
35874     /**
35875      * Hides this menu and optionally all parent menus
35876      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35877      */
35878     hide : function(deep){
35879         if(this.el && this.isVisible()){
35880             this.fireEvent("beforehide", this);
35881             if(this.activeItem){
35882                 this.activeItem.deactivate();
35883                 this.activeItem = null;
35884             }
35885             this.el.hide();
35886             this.hidden = true;
35887             this.fireEvent("hide", this);
35888         }
35889         if(deep === true && this.parentMenu){
35890             this.parentMenu.hide(true);
35891         }
35892     },
35893
35894     /**
35895      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35896      * Any of the following are valid:
35897      * <ul>
35898      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35899      * <li>An HTMLElement object which will be converted to a menu item</li>
35900      * <li>A menu item config object that will be created as a new menu item</li>
35901      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35902      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35903      * </ul>
35904      * Usage:
35905      * <pre><code>
35906 // Create the menu
35907 var menu = new Roo.menu.Menu();
35908
35909 // Create a menu item to add by reference
35910 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35911
35912 // Add a bunch of items at once using different methods.
35913 // Only the last item added will be returned.
35914 var item = menu.add(
35915     menuItem,                // add existing item by ref
35916     'Dynamic Item',          // new TextItem
35917     '-',                     // new separator
35918     { text: 'Config Item' }  // new item by config
35919 );
35920 </code></pre>
35921      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35922      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35923      */
35924     add : function(){
35925         var a = arguments, l = a.length, item;
35926         for(var i = 0; i < l; i++){
35927             var el = a[i];
35928             if ((typeof(el) == "object") && el.xtype && el.xns) {
35929                 el = Roo.factory(el, Roo.menu);
35930             }
35931             
35932             if(el.render){ // some kind of Item
35933                 item = this.addItem(el);
35934             }else if(typeof el == "string"){ // string
35935                 if(el == "separator" || el == "-"){
35936                     item = this.addSeparator();
35937                 }else{
35938                     item = this.addText(el);
35939                 }
35940             }else if(el.tagName || el.el){ // element
35941                 item = this.addElement(el);
35942             }else if(typeof el == "object"){ // must be menu item config?
35943                 item = this.addMenuItem(el);
35944             }
35945         }
35946         return item;
35947     },
35948
35949     /**
35950      * Returns this menu's underlying {@link Roo.Element} object
35951      * @return {Roo.Element} The element
35952      */
35953     getEl : function(){
35954         if(!this.el){
35955             this.render();
35956         }
35957         return this.el;
35958     },
35959
35960     /**
35961      * Adds a separator bar to the menu
35962      * @return {Roo.menu.Item} The menu item that was added
35963      */
35964     addSeparator : function(){
35965         return this.addItem(new Roo.menu.Separator());
35966     },
35967
35968     /**
35969      * Adds an {@link Roo.Element} object to the menu
35970      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35971      * @return {Roo.menu.Item} The menu item that was added
35972      */
35973     addElement : function(el){
35974         return this.addItem(new Roo.menu.BaseItem(el));
35975     },
35976
35977     /**
35978      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35979      * @param {Roo.menu.Item} item The menu item to add
35980      * @return {Roo.menu.Item} The menu item that was added
35981      */
35982     addItem : function(item){
35983         this.items.add(item);
35984         if(this.ul){
35985             var li = document.createElement("li");
35986             li.className = "x-menu-list-item";
35987             this.ul.dom.appendChild(li);
35988             item.render(li, this);
35989             this.delayAutoWidth();
35990         }
35991         return item;
35992     },
35993
35994     /**
35995      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35996      * @param {Object} config A MenuItem config object
35997      * @return {Roo.menu.Item} The menu item that was added
35998      */
35999     addMenuItem : function(config){
36000         if(!(config instanceof Roo.menu.Item)){
36001             if(typeof config.checked == "boolean"){ // must be check menu item config?
36002                 config = new Roo.menu.CheckItem(config);
36003             }else{
36004                 config = new Roo.menu.Item(config);
36005             }
36006         }
36007         return this.addItem(config);
36008     },
36009
36010     /**
36011      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36012      * @param {String} text The text to display in the menu item
36013      * @return {Roo.menu.Item} The menu item that was added
36014      */
36015     addText : function(text){
36016         return this.addItem(new Roo.menu.TextItem({ text : text }));
36017     },
36018
36019     /**
36020      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36021      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36022      * @param {Roo.menu.Item} item The menu item to add
36023      * @return {Roo.menu.Item} The menu item that was added
36024      */
36025     insert : function(index, item){
36026         this.items.insert(index, item);
36027         if(this.ul){
36028             var li = document.createElement("li");
36029             li.className = "x-menu-list-item";
36030             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36031             item.render(li, this);
36032             this.delayAutoWidth();
36033         }
36034         return item;
36035     },
36036
36037     /**
36038      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36039      * @param {Roo.menu.Item} item The menu item to remove
36040      */
36041     remove : function(item){
36042         this.items.removeKey(item.id);
36043         item.destroy();
36044     },
36045
36046     /**
36047      * Removes and destroys all items in the menu
36048      */
36049     removeAll : function(){
36050         var f;
36051         while(f = this.items.first()){
36052             this.remove(f);
36053         }
36054     }
36055 });
36056
36057 // MenuNav is a private utility class used internally by the Menu
36058 Roo.menu.MenuNav = function(menu){
36059     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36060     this.scope = this.menu = menu;
36061 };
36062
36063 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36064     doRelay : function(e, h){
36065         var k = e.getKey();
36066         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36067             this.menu.tryActivate(0, 1);
36068             return false;
36069         }
36070         return h.call(this.scope || this, e, this.menu);
36071     },
36072
36073     up : function(e, m){
36074         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36075             m.tryActivate(m.items.length-1, -1);
36076         }
36077     },
36078
36079     down : function(e, m){
36080         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36081             m.tryActivate(0, 1);
36082         }
36083     },
36084
36085     right : function(e, m){
36086         if(m.activeItem){
36087             m.activeItem.expandMenu(true);
36088         }
36089     },
36090
36091     left : function(e, m){
36092         m.hide();
36093         if(m.parentMenu && m.parentMenu.activeItem){
36094             m.parentMenu.activeItem.activate();
36095         }
36096     },
36097
36098     enter : function(e, m){
36099         if(m.activeItem){
36100             e.stopPropagation();
36101             m.activeItem.onClick(e);
36102             m.fireEvent("click", this, m.activeItem);
36103             return true;
36104         }
36105     }
36106 });/*
36107  * Based on:
36108  * Ext JS Library 1.1.1
36109  * Copyright(c) 2006-2007, Ext JS, LLC.
36110  *
36111  * Originally Released Under LGPL - original licence link has changed is not relivant.
36112  *
36113  * Fork - LGPL
36114  * <script type="text/javascript">
36115  */
36116  
36117 /**
36118  * @class Roo.menu.MenuMgr
36119  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36120  * @singleton
36121  */
36122 Roo.menu.MenuMgr = function(){
36123    var menus, active, groups = {}, attached = false, lastShow = new Date();
36124
36125    // private - called when first menu is created
36126    function init(){
36127        menus = {};
36128        active = new Roo.util.MixedCollection();
36129        Roo.get(document).addKeyListener(27, function(){
36130            if(active.length > 0){
36131                hideAll();
36132            }
36133        });
36134    }
36135
36136    // private
36137    function hideAll(){
36138        if(active && active.length > 0){
36139            var c = active.clone();
36140            c.each(function(m){
36141                m.hide();
36142            });
36143        }
36144    }
36145
36146    // private
36147    function onHide(m){
36148        active.remove(m);
36149        if(active.length < 1){
36150            Roo.get(document).un("mousedown", onMouseDown);
36151            attached = false;
36152        }
36153    }
36154
36155    // private
36156    function onShow(m){
36157        var last = active.last();
36158        lastShow = new Date();
36159        active.add(m);
36160        if(!attached){
36161            Roo.get(document).on("mousedown", onMouseDown);
36162            attached = true;
36163        }
36164        if(m.parentMenu){
36165           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36166           m.parentMenu.activeChild = m;
36167        }else if(last && last.isVisible()){
36168           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36169        }
36170    }
36171
36172    // private
36173    function onBeforeHide(m){
36174        if(m.activeChild){
36175            m.activeChild.hide();
36176        }
36177        if(m.autoHideTimer){
36178            clearTimeout(m.autoHideTimer);
36179            delete m.autoHideTimer;
36180        }
36181    }
36182
36183    // private
36184    function onBeforeShow(m){
36185        var pm = m.parentMenu;
36186        if(!pm && !m.allowOtherMenus){
36187            hideAll();
36188        }else if(pm && pm.activeChild && active != m){
36189            pm.activeChild.hide();
36190        }
36191    }
36192
36193    // private
36194    function onMouseDown(e){
36195        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36196            hideAll();
36197        }
36198    }
36199
36200    // private
36201    function onBeforeCheck(mi, state){
36202        if(state){
36203            var g = groups[mi.group];
36204            for(var i = 0, l = g.length; i < l; i++){
36205                if(g[i] != mi){
36206                    g[i].setChecked(false);
36207                }
36208            }
36209        }
36210    }
36211
36212    return {
36213
36214        /**
36215         * Hides all menus that are currently visible
36216         */
36217        hideAll : function(){
36218             hideAll();  
36219        },
36220
36221        // private
36222        register : function(menu){
36223            if(!menus){
36224                init();
36225            }
36226            menus[menu.id] = menu;
36227            menu.on("beforehide", onBeforeHide);
36228            menu.on("hide", onHide);
36229            menu.on("beforeshow", onBeforeShow);
36230            menu.on("show", onShow);
36231            var g = menu.group;
36232            if(g && menu.events["checkchange"]){
36233                if(!groups[g]){
36234                    groups[g] = [];
36235                }
36236                groups[g].push(menu);
36237                menu.on("checkchange", onCheck);
36238            }
36239        },
36240
36241         /**
36242          * Returns a {@link Roo.menu.Menu} object
36243          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36244          * be used to generate and return a new Menu instance.
36245          */
36246        get : function(menu){
36247            if(typeof menu == "string"){ // menu id
36248                return menus[menu];
36249            }else if(menu.events){  // menu instance
36250                return menu;
36251            }else if(typeof menu.length == 'number'){ // array of menu items?
36252                return new Roo.menu.Menu({items:menu});
36253            }else{ // otherwise, must be a config
36254                return new Roo.menu.Menu(menu);
36255            }
36256        },
36257
36258        // private
36259        unregister : function(menu){
36260            delete menus[menu.id];
36261            menu.un("beforehide", onBeforeHide);
36262            menu.un("hide", onHide);
36263            menu.un("beforeshow", onBeforeShow);
36264            menu.un("show", onShow);
36265            var g = menu.group;
36266            if(g && menu.events["checkchange"]){
36267                groups[g].remove(menu);
36268                menu.un("checkchange", onCheck);
36269            }
36270        },
36271
36272        // private
36273        registerCheckable : function(menuItem){
36274            var g = menuItem.group;
36275            if(g){
36276                if(!groups[g]){
36277                    groups[g] = [];
36278                }
36279                groups[g].push(menuItem);
36280                menuItem.on("beforecheckchange", onBeforeCheck);
36281            }
36282        },
36283
36284        // private
36285        unregisterCheckable : function(menuItem){
36286            var g = menuItem.group;
36287            if(g){
36288                groups[g].remove(menuItem);
36289                menuItem.un("beforecheckchange", onBeforeCheck);
36290            }
36291        }
36292    };
36293 }();/*
36294  * Based on:
36295  * Ext JS Library 1.1.1
36296  * Copyright(c) 2006-2007, Ext JS, LLC.
36297  *
36298  * Originally Released Under LGPL - original licence link has changed is not relivant.
36299  *
36300  * Fork - LGPL
36301  * <script type="text/javascript">
36302  */
36303  
36304
36305 /**
36306  * @class Roo.menu.BaseItem
36307  * @extends Roo.Component
36308  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36309  * management and base configuration options shared by all menu components.
36310  * @constructor
36311  * Creates a new BaseItem
36312  * @param {Object} config Configuration options
36313  */
36314 Roo.menu.BaseItem = function(config){
36315     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36316
36317     this.addEvents({
36318         /**
36319          * @event click
36320          * Fires when this item is clicked
36321          * @param {Roo.menu.BaseItem} this
36322          * @param {Roo.EventObject} e
36323          */
36324         click: true,
36325         /**
36326          * @event activate
36327          * Fires when this item is activated
36328          * @param {Roo.menu.BaseItem} this
36329          */
36330         activate : true,
36331         /**
36332          * @event deactivate
36333          * Fires when this item is deactivated
36334          * @param {Roo.menu.BaseItem} this
36335          */
36336         deactivate : true
36337     });
36338
36339     if(this.handler){
36340         this.on("click", this.handler, this.scope, true);
36341     }
36342 };
36343
36344 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36345     /**
36346      * @cfg {Function} handler
36347      * A function that will handle the click event of this menu item (defaults to undefined)
36348      */
36349     /**
36350      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36351      */
36352     canActivate : false,
36353     
36354      /**
36355      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36356      */
36357     hidden: false,
36358     
36359     /**
36360      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36361      */
36362     activeClass : "x-menu-item-active",
36363     /**
36364      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36365      */
36366     hideOnClick : true,
36367     /**
36368      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36369      */
36370     hideDelay : 100,
36371
36372     // private
36373     ctype: "Roo.menu.BaseItem",
36374
36375     // private
36376     actionMode : "container",
36377
36378     // private
36379     render : function(container, parentMenu){
36380         this.parentMenu = parentMenu;
36381         Roo.menu.BaseItem.superclass.render.call(this, container);
36382         this.container.menuItemId = this.id;
36383     },
36384
36385     // private
36386     onRender : function(container, position){
36387         this.el = Roo.get(this.el);
36388         container.dom.appendChild(this.el.dom);
36389     },
36390
36391     // private
36392     onClick : function(e){
36393         if(!this.disabled && this.fireEvent("click", this, e) !== false
36394                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36395             this.handleClick(e);
36396         }else{
36397             e.stopEvent();
36398         }
36399     },
36400
36401     // private
36402     activate : function(){
36403         if(this.disabled){
36404             return false;
36405         }
36406         var li = this.container;
36407         li.addClass(this.activeClass);
36408         this.region = li.getRegion().adjust(2, 2, -2, -2);
36409         this.fireEvent("activate", this);
36410         return true;
36411     },
36412
36413     // private
36414     deactivate : function(){
36415         this.container.removeClass(this.activeClass);
36416         this.fireEvent("deactivate", this);
36417     },
36418
36419     // private
36420     shouldDeactivate : function(e){
36421         return !this.region || !this.region.contains(e.getPoint());
36422     },
36423
36424     // private
36425     handleClick : function(e){
36426         if(this.hideOnClick){
36427             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36428         }
36429     },
36430
36431     // private
36432     expandMenu : function(autoActivate){
36433         // do nothing
36434     },
36435
36436     // private
36437     hideMenu : function(){
36438         // do nothing
36439     }
36440 });/*
36441  * Based on:
36442  * Ext JS Library 1.1.1
36443  * Copyright(c) 2006-2007, Ext JS, LLC.
36444  *
36445  * Originally Released Under LGPL - original licence link has changed is not relivant.
36446  *
36447  * Fork - LGPL
36448  * <script type="text/javascript">
36449  */
36450  
36451 /**
36452  * @class Roo.menu.Adapter
36453  * @extends Roo.menu.BaseItem
36454  * 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.
36455  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36456  * @constructor
36457  * Creates a new Adapter
36458  * @param {Object} config Configuration options
36459  */
36460 Roo.menu.Adapter = function(component, config){
36461     Roo.menu.Adapter.superclass.constructor.call(this, config);
36462     this.component = component;
36463 };
36464 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36465     // private
36466     canActivate : true,
36467
36468     // private
36469     onRender : function(container, position){
36470         this.component.render(container);
36471         this.el = this.component.getEl();
36472     },
36473
36474     // private
36475     activate : function(){
36476         if(this.disabled){
36477             return false;
36478         }
36479         this.component.focus();
36480         this.fireEvent("activate", this);
36481         return true;
36482     },
36483
36484     // private
36485     deactivate : function(){
36486         this.fireEvent("deactivate", this);
36487     },
36488
36489     // private
36490     disable : function(){
36491         this.component.disable();
36492         Roo.menu.Adapter.superclass.disable.call(this);
36493     },
36494
36495     // private
36496     enable : function(){
36497         this.component.enable();
36498         Roo.menu.Adapter.superclass.enable.call(this);
36499     }
36500 });/*
36501  * Based on:
36502  * Ext JS Library 1.1.1
36503  * Copyright(c) 2006-2007, Ext JS, LLC.
36504  *
36505  * Originally Released Under LGPL - original licence link has changed is not relivant.
36506  *
36507  * Fork - LGPL
36508  * <script type="text/javascript">
36509  */
36510
36511 /**
36512  * @class Roo.menu.TextItem
36513  * @extends Roo.menu.BaseItem
36514  * Adds a static text string to a menu, usually used as either a heading or group separator.
36515  * Note: old style constructor with text is still supported.
36516  * 
36517  * @constructor
36518  * Creates a new TextItem
36519  * @param {Object} cfg Configuration
36520  */
36521 Roo.menu.TextItem = function(cfg){
36522     if (typeof(cfg) == 'string') {
36523         this.text = cfg;
36524     } else {
36525         Roo.apply(this,cfg);
36526     }
36527     
36528     Roo.menu.TextItem.superclass.constructor.call(this);
36529 };
36530
36531 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36532     /**
36533      * @cfg {Boolean} text Text to show on item.
36534      */
36535     text : '',
36536     
36537     /**
36538      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36539      */
36540     hideOnClick : false,
36541     /**
36542      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36543      */
36544     itemCls : "x-menu-text",
36545
36546     // private
36547     onRender : function(){
36548         var s = document.createElement("span");
36549         s.className = this.itemCls;
36550         s.innerHTML = this.text;
36551         this.el = s;
36552         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36553     }
36554 });/*
36555  * Based on:
36556  * Ext JS Library 1.1.1
36557  * Copyright(c) 2006-2007, Ext JS, LLC.
36558  *
36559  * Originally Released Under LGPL - original licence link has changed is not relivant.
36560  *
36561  * Fork - LGPL
36562  * <script type="text/javascript">
36563  */
36564
36565 /**
36566  * @class Roo.menu.Separator
36567  * @extends Roo.menu.BaseItem
36568  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36569  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36570  * @constructor
36571  * @param {Object} config Configuration options
36572  */
36573 Roo.menu.Separator = function(config){
36574     Roo.menu.Separator.superclass.constructor.call(this, config);
36575 };
36576
36577 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36578     /**
36579      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36580      */
36581     itemCls : "x-menu-sep",
36582     /**
36583      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36584      */
36585     hideOnClick : false,
36586
36587     // private
36588     onRender : function(li){
36589         var s = document.createElement("span");
36590         s.className = this.itemCls;
36591         s.innerHTML = "&#160;";
36592         this.el = s;
36593         li.addClass("x-menu-sep-li");
36594         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36595     }
36596 });/*
36597  * Based on:
36598  * Ext JS Library 1.1.1
36599  * Copyright(c) 2006-2007, Ext JS, LLC.
36600  *
36601  * Originally Released Under LGPL - original licence link has changed is not relivant.
36602  *
36603  * Fork - LGPL
36604  * <script type="text/javascript">
36605  */
36606 /**
36607  * @class Roo.menu.Item
36608  * @extends Roo.menu.BaseItem
36609  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36610  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36611  * activation and click handling.
36612  * @constructor
36613  * Creates a new Item
36614  * @param {Object} config Configuration options
36615  */
36616 Roo.menu.Item = function(config){
36617     Roo.menu.Item.superclass.constructor.call(this, config);
36618     if(this.menu){
36619         this.menu = Roo.menu.MenuMgr.get(this.menu);
36620     }
36621 };
36622 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36623     
36624     /**
36625      * @cfg {String} text
36626      * The text to show on the menu item.
36627      */
36628     text: '',
36629      /**
36630      * @cfg {String} HTML to render in menu
36631      * The text to show on the menu item (HTML version).
36632      */
36633     html: '',
36634     /**
36635      * @cfg {String} icon
36636      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36637      */
36638     icon: undefined,
36639     /**
36640      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36641      */
36642     itemCls : "x-menu-item",
36643     /**
36644      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36645      */
36646     canActivate : true,
36647     /**
36648      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36649      */
36650     showDelay: 200,
36651     // doc'd in BaseItem
36652     hideDelay: 200,
36653
36654     // private
36655     ctype: "Roo.menu.Item",
36656     
36657     // private
36658     onRender : function(container, position){
36659         var el = document.createElement("a");
36660         el.hideFocus = true;
36661         el.unselectable = "on";
36662         el.href = this.href || "#";
36663         if(this.hrefTarget){
36664             el.target = this.hrefTarget;
36665         }
36666         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36667         
36668         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36669         
36670         el.innerHTML = String.format(
36671                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36672                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36673         this.el = el;
36674         Roo.menu.Item.superclass.onRender.call(this, container, position);
36675     },
36676
36677     /**
36678      * Sets the text to display in this menu item
36679      * @param {String} text The text to display
36680      * @param {Boolean} isHTML true to indicate text is pure html.
36681      */
36682     setText : function(text, isHTML){
36683         if (isHTML) {
36684             this.html = text;
36685         } else {
36686             this.text = text;
36687             this.html = '';
36688         }
36689         if(this.rendered){
36690             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36691      
36692             this.el.update(String.format(
36693                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36694                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36695             this.parentMenu.autoWidth();
36696         }
36697     },
36698
36699     // private
36700     handleClick : function(e){
36701         if(!this.href){ // if no link defined, stop the event automatically
36702             e.stopEvent();
36703         }
36704         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36705     },
36706
36707     // private
36708     activate : function(autoExpand){
36709         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36710             this.focus();
36711             if(autoExpand){
36712                 this.expandMenu();
36713             }
36714         }
36715         return true;
36716     },
36717
36718     // private
36719     shouldDeactivate : function(e){
36720         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36721             if(this.menu && this.menu.isVisible()){
36722                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36723             }
36724             return true;
36725         }
36726         return false;
36727     },
36728
36729     // private
36730     deactivate : function(){
36731         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36732         this.hideMenu();
36733     },
36734
36735     // private
36736     expandMenu : function(autoActivate){
36737         if(!this.disabled && this.menu){
36738             clearTimeout(this.hideTimer);
36739             delete this.hideTimer;
36740             if(!this.menu.isVisible() && !this.showTimer){
36741                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36742             }else if (this.menu.isVisible() && autoActivate){
36743                 this.menu.tryActivate(0, 1);
36744             }
36745         }
36746     },
36747
36748     // private
36749     deferExpand : function(autoActivate){
36750         delete this.showTimer;
36751         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36752         if(autoActivate){
36753             this.menu.tryActivate(0, 1);
36754         }
36755     },
36756
36757     // private
36758     hideMenu : function(){
36759         clearTimeout(this.showTimer);
36760         delete this.showTimer;
36761         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36762             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36763         }
36764     },
36765
36766     // private
36767     deferHide : function(){
36768         delete this.hideTimer;
36769         this.menu.hide();
36770     }
36771 });/*
36772  * Based on:
36773  * Ext JS Library 1.1.1
36774  * Copyright(c) 2006-2007, Ext JS, LLC.
36775  *
36776  * Originally Released Under LGPL - original licence link has changed is not relivant.
36777  *
36778  * Fork - LGPL
36779  * <script type="text/javascript">
36780  */
36781  
36782 /**
36783  * @class Roo.menu.CheckItem
36784  * @extends Roo.menu.Item
36785  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36786  * @constructor
36787  * Creates a new CheckItem
36788  * @param {Object} config Configuration options
36789  */
36790 Roo.menu.CheckItem = function(config){
36791     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36792     this.addEvents({
36793         /**
36794          * @event beforecheckchange
36795          * Fires before the checked value is set, providing an opportunity to cancel if needed
36796          * @param {Roo.menu.CheckItem} this
36797          * @param {Boolean} checked The new checked value that will be set
36798          */
36799         "beforecheckchange" : true,
36800         /**
36801          * @event checkchange
36802          * Fires after the checked value has been set
36803          * @param {Roo.menu.CheckItem} this
36804          * @param {Boolean} checked The checked value that was set
36805          */
36806         "checkchange" : true
36807     });
36808     if(this.checkHandler){
36809         this.on('checkchange', this.checkHandler, this.scope);
36810     }
36811 };
36812 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36813     /**
36814      * @cfg {String} group
36815      * All check items with the same group name will automatically be grouped into a single-select
36816      * radio button group (defaults to '')
36817      */
36818     /**
36819      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36820      */
36821     itemCls : "x-menu-item x-menu-check-item",
36822     /**
36823      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36824      */
36825     groupClass : "x-menu-group-item",
36826
36827     /**
36828      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36829      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36830      * initialized with checked = true will be rendered as checked.
36831      */
36832     checked: false,
36833
36834     // private
36835     ctype: "Roo.menu.CheckItem",
36836
36837     // private
36838     onRender : function(c){
36839         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36840         if(this.group){
36841             this.el.addClass(this.groupClass);
36842         }
36843         Roo.menu.MenuMgr.registerCheckable(this);
36844         if(this.checked){
36845             this.checked = false;
36846             this.setChecked(true, true);
36847         }
36848     },
36849
36850     // private
36851     destroy : function(){
36852         if(this.rendered){
36853             Roo.menu.MenuMgr.unregisterCheckable(this);
36854         }
36855         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36856     },
36857
36858     /**
36859      * Set the checked state of this item
36860      * @param {Boolean} checked The new checked value
36861      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36862      */
36863     setChecked : function(state, suppressEvent){
36864         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36865             if(this.container){
36866                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36867             }
36868             this.checked = state;
36869             if(suppressEvent !== true){
36870                 this.fireEvent("checkchange", this, state);
36871             }
36872         }
36873     },
36874
36875     // private
36876     handleClick : function(e){
36877        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36878            this.setChecked(!this.checked);
36879        }
36880        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36881     }
36882 });/*
36883  * Based on:
36884  * Ext JS Library 1.1.1
36885  * Copyright(c) 2006-2007, Ext JS, LLC.
36886  *
36887  * Originally Released Under LGPL - original licence link has changed is not relivant.
36888  *
36889  * Fork - LGPL
36890  * <script type="text/javascript">
36891  */
36892  
36893 /**
36894  * @class Roo.menu.DateItem
36895  * @extends Roo.menu.Adapter
36896  * A menu item that wraps the {@link Roo.DatPicker} component.
36897  * @constructor
36898  * Creates a new DateItem
36899  * @param {Object} config Configuration options
36900  */
36901 Roo.menu.DateItem = function(config){
36902     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36903     /** The Roo.DatePicker object @type Roo.DatePicker */
36904     this.picker = this.component;
36905     this.addEvents({select: true});
36906     
36907     this.picker.on("render", function(picker){
36908         picker.getEl().swallowEvent("click");
36909         picker.container.addClass("x-menu-date-item");
36910     });
36911
36912     this.picker.on("select", this.onSelect, this);
36913 };
36914
36915 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36916     // private
36917     onSelect : function(picker, date){
36918         this.fireEvent("select", this, date, picker);
36919         Roo.menu.DateItem.superclass.handleClick.call(this);
36920     }
36921 });/*
36922  * Based on:
36923  * Ext JS Library 1.1.1
36924  * Copyright(c) 2006-2007, Ext JS, LLC.
36925  *
36926  * Originally Released Under LGPL - original licence link has changed is not relivant.
36927  *
36928  * Fork - LGPL
36929  * <script type="text/javascript">
36930  */
36931  
36932 /**
36933  * @class Roo.menu.ColorItem
36934  * @extends Roo.menu.Adapter
36935  * A menu item that wraps the {@link Roo.ColorPalette} component.
36936  * @constructor
36937  * Creates a new ColorItem
36938  * @param {Object} config Configuration options
36939  */
36940 Roo.menu.ColorItem = function(config){
36941     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36942     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36943     this.palette = this.component;
36944     this.relayEvents(this.palette, ["select"]);
36945     if(this.selectHandler){
36946         this.on('select', this.selectHandler, this.scope);
36947     }
36948 };
36949 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36950  * Based on:
36951  * Ext JS Library 1.1.1
36952  * Copyright(c) 2006-2007, Ext JS, LLC.
36953  *
36954  * Originally Released Under LGPL - original licence link has changed is not relivant.
36955  *
36956  * Fork - LGPL
36957  * <script type="text/javascript">
36958  */
36959  
36960
36961 /**
36962  * @class Roo.menu.DateMenu
36963  * @extends Roo.menu.Menu
36964  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36965  * @constructor
36966  * Creates a new DateMenu
36967  * @param {Object} config Configuration options
36968  */
36969 Roo.menu.DateMenu = function(config){
36970     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36971     this.plain = true;
36972     var di = new Roo.menu.DateItem(config);
36973     this.add(di);
36974     /**
36975      * The {@link Roo.DatePicker} instance for this DateMenu
36976      * @type DatePicker
36977      */
36978     this.picker = di.picker;
36979     /**
36980      * @event select
36981      * @param {DatePicker} picker
36982      * @param {Date} date
36983      */
36984     this.relayEvents(di, ["select"]);
36985     this.on('beforeshow', function(){
36986         if(this.picker){
36987             this.picker.hideMonthPicker(false);
36988         }
36989     }, this);
36990 };
36991 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36992     cls:'x-date-menu'
36993 });/*
36994  * Based on:
36995  * Ext JS Library 1.1.1
36996  * Copyright(c) 2006-2007, Ext JS, LLC.
36997  *
36998  * Originally Released Under LGPL - original licence link has changed is not relivant.
36999  *
37000  * Fork - LGPL
37001  * <script type="text/javascript">
37002  */
37003  
37004
37005 /**
37006  * @class Roo.menu.ColorMenu
37007  * @extends Roo.menu.Menu
37008  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37009  * @constructor
37010  * Creates a new ColorMenu
37011  * @param {Object} config Configuration options
37012  */
37013 Roo.menu.ColorMenu = function(config){
37014     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37015     this.plain = true;
37016     var ci = new Roo.menu.ColorItem(config);
37017     this.add(ci);
37018     /**
37019      * The {@link Roo.ColorPalette} instance for this ColorMenu
37020      * @type ColorPalette
37021      */
37022     this.palette = ci.palette;
37023     /**
37024      * @event select
37025      * @param {ColorPalette} palette
37026      * @param {String} color
37027      */
37028     this.relayEvents(ci, ["select"]);
37029 };
37030 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37031  * Based on:
37032  * Ext JS Library 1.1.1
37033  * Copyright(c) 2006-2007, Ext JS, LLC.
37034  *
37035  * Originally Released Under LGPL - original licence link has changed is not relivant.
37036  *
37037  * Fork - LGPL
37038  * <script type="text/javascript">
37039  */
37040  
37041 /**
37042  * @class Roo.form.Field
37043  * @extends Roo.BoxComponent
37044  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37045  * @constructor
37046  * Creates a new Field
37047  * @param {Object} config Configuration options
37048  */
37049 Roo.form.Field = function(config){
37050     Roo.form.Field.superclass.constructor.call(this, config);
37051 };
37052
37053 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37054     /**
37055      * @cfg {String} fieldLabel Label to use when rendering a form.
37056      */
37057        /**
37058      * @cfg {String} qtip Mouse over tip
37059      */
37060      
37061     /**
37062      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37063      */
37064     invalidClass : "x-form-invalid",
37065     /**
37066      * @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")
37067      */
37068     invalidText : "The value in this field is invalid",
37069     /**
37070      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37071      */
37072     focusClass : "x-form-focus",
37073     /**
37074      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37075       automatic validation (defaults to "keyup").
37076      */
37077     validationEvent : "keyup",
37078     /**
37079      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37080      */
37081     validateOnBlur : true,
37082     /**
37083      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37084      */
37085     validationDelay : 250,
37086     /**
37087      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37088      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37089      */
37090     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37091     /**
37092      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37093      */
37094     fieldClass : "x-form-field",
37095     /**
37096      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37097      *<pre>
37098 Value         Description
37099 -----------   ----------------------------------------------------------------------
37100 qtip          Display a quick tip when the user hovers over the field
37101 title         Display a default browser title attribute popup
37102 under         Add a block div beneath the field containing the error text
37103 side          Add an error icon to the right of the field with a popup on hover
37104 [element id]  Add the error text directly to the innerHTML of the specified element
37105 </pre>
37106      */
37107     msgTarget : 'qtip',
37108     /**
37109      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37110      */
37111     msgFx : 'normal',
37112
37113     /**
37114      * @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.
37115      */
37116     readOnly : false,
37117
37118     /**
37119      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37120      */
37121     disabled : false,
37122
37123     /**
37124      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37125      */
37126     inputType : undefined,
37127     
37128     /**
37129      * @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).
37130          */
37131         tabIndex : undefined,
37132         
37133     // private
37134     isFormField : true,
37135
37136     // private
37137     hasFocus : false,
37138     /**
37139      * @property {Roo.Element} fieldEl
37140      * Element Containing the rendered Field (with label etc.)
37141      */
37142     /**
37143      * @cfg {Mixed} value A value to initialize this field with.
37144      */
37145     value : undefined,
37146
37147     /**
37148      * @cfg {String} name The field's HTML name attribute.
37149      */
37150     /**
37151      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37152      */
37153
37154         // private ??
37155         initComponent : function(){
37156         Roo.form.Field.superclass.initComponent.call(this);
37157         this.addEvents({
37158             /**
37159              * @event focus
37160              * Fires when this field receives input focus.
37161              * @param {Roo.form.Field} this
37162              */
37163             focus : true,
37164             /**
37165              * @event blur
37166              * Fires when this field loses input focus.
37167              * @param {Roo.form.Field} this
37168              */
37169             blur : true,
37170             /**
37171              * @event specialkey
37172              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37173              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37174              * @param {Roo.form.Field} this
37175              * @param {Roo.EventObject} e The event object
37176              */
37177             specialkey : true,
37178             /**
37179              * @event change
37180              * Fires just before the field blurs if the field value has changed.
37181              * @param {Roo.form.Field} this
37182              * @param {Mixed} newValue The new value
37183              * @param {Mixed} oldValue The original value
37184              */
37185             change : true,
37186             /**
37187              * @event invalid
37188              * Fires after the field has been marked as invalid.
37189              * @param {Roo.form.Field} this
37190              * @param {String} msg The validation message
37191              */
37192             invalid : true,
37193             /**
37194              * @event valid
37195              * Fires after the field has been validated with no errors.
37196              * @param {Roo.form.Field} this
37197              */
37198             valid : true,
37199              /**
37200              * @event keyup
37201              * Fires after the key up
37202              * @param {Roo.form.Field} this
37203              * @param {Roo.EventObject}  e The event Object
37204              */
37205             keyup : true
37206         });
37207     },
37208
37209     /**
37210      * Returns the name attribute of the field if available
37211      * @return {String} name The field name
37212      */
37213     getName: function(){
37214          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37215     },
37216
37217     // private
37218     onRender : function(ct, position){
37219         Roo.form.Field.superclass.onRender.call(this, ct, position);
37220         if(!this.el){
37221             var cfg = this.getAutoCreate();
37222             if(!cfg.name){
37223                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37224             }
37225             if (!cfg.name.length) {
37226                 delete cfg.name;
37227             }
37228             if(this.inputType){
37229                 cfg.type = this.inputType;
37230             }
37231             this.el = ct.createChild(cfg, position);
37232         }
37233         var type = this.el.dom.type;
37234         if(type){
37235             if(type == 'password'){
37236                 type = 'text';
37237             }
37238             this.el.addClass('x-form-'+type);
37239         }
37240         if(this.readOnly){
37241             this.el.dom.readOnly = true;
37242         }
37243         if(this.tabIndex !== undefined){
37244             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37245         }
37246
37247         this.el.addClass([this.fieldClass, this.cls]);
37248         this.initValue();
37249     },
37250
37251     /**
37252      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37253      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37254      * @return {Roo.form.Field} this
37255      */
37256     applyTo : function(target){
37257         this.allowDomMove = false;
37258         this.el = Roo.get(target);
37259         this.render(this.el.dom.parentNode);
37260         return this;
37261     },
37262
37263     // private
37264     initValue : function(){
37265         if(this.value !== undefined){
37266             this.setValue(this.value);
37267         }else if(this.el.dom.value.length > 0){
37268             this.setValue(this.el.dom.value);
37269         }
37270     },
37271
37272     /**
37273      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37274      */
37275     isDirty : function() {
37276         if(this.disabled) {
37277             return false;
37278         }
37279         return String(this.getValue()) !== String(this.originalValue);
37280     },
37281
37282     // private
37283     afterRender : function(){
37284         Roo.form.Field.superclass.afterRender.call(this);
37285         this.initEvents();
37286     },
37287
37288     // private
37289     fireKey : function(e){
37290         //Roo.log('field ' + e.getKey());
37291         if(e.isNavKeyPress()){
37292             this.fireEvent("specialkey", this, e);
37293         }
37294     },
37295
37296     /**
37297      * Resets the current field value to the originally loaded value and clears any validation messages
37298      */
37299     reset : function(){
37300         this.setValue(this.resetValue);
37301         this.clearInvalid();
37302     },
37303
37304     // private
37305     initEvents : function(){
37306         // safari killled keypress - so keydown is now used..
37307         this.el.on("keydown" , this.fireKey,  this);
37308         this.el.on("focus", this.onFocus,  this);
37309         this.el.on("blur", this.onBlur,  this);
37310         this.el.relayEvent('keyup', this);
37311
37312         // reference to original value for reset
37313         this.originalValue = this.getValue();
37314         this.resetValue =  this.getValue();
37315     },
37316
37317     // private
37318     onFocus : function(){
37319         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37320             this.el.addClass(this.focusClass);
37321         }
37322         if(!this.hasFocus){
37323             this.hasFocus = true;
37324             this.startValue = this.getValue();
37325             this.fireEvent("focus", this);
37326         }
37327     },
37328
37329     beforeBlur : Roo.emptyFn,
37330
37331     // private
37332     onBlur : function(){
37333         this.beforeBlur();
37334         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37335             this.el.removeClass(this.focusClass);
37336         }
37337         this.hasFocus = false;
37338         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37339             this.validate();
37340         }
37341         var v = this.getValue();
37342         if(String(v) !== String(this.startValue)){
37343             this.fireEvent('change', this, v, this.startValue);
37344         }
37345         this.fireEvent("blur", this);
37346     },
37347
37348     /**
37349      * Returns whether or not the field value is currently valid
37350      * @param {Boolean} preventMark True to disable marking the field invalid
37351      * @return {Boolean} True if the value is valid, else false
37352      */
37353     isValid : function(preventMark){
37354         if(this.disabled){
37355             return true;
37356         }
37357         var restore = this.preventMark;
37358         this.preventMark = preventMark === true;
37359         var v = this.validateValue(this.processValue(this.getRawValue()));
37360         this.preventMark = restore;
37361         return v;
37362     },
37363
37364     /**
37365      * Validates the field value
37366      * @return {Boolean} True if the value is valid, else false
37367      */
37368     validate : function(){
37369         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37370             this.clearInvalid();
37371             return true;
37372         }
37373         return false;
37374     },
37375
37376     processValue : function(value){
37377         return value;
37378     },
37379
37380     // private
37381     // Subclasses should provide the validation implementation by overriding this
37382     validateValue : function(value){
37383         return true;
37384     },
37385
37386     /**
37387      * Mark this field as invalid
37388      * @param {String} msg The validation message
37389      */
37390     markInvalid : function(msg){
37391         if(!this.rendered || this.preventMark){ // not rendered
37392             return;
37393         }
37394         
37395         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37396         
37397         obj.el.addClass(this.invalidClass);
37398         msg = msg || this.invalidText;
37399         switch(this.msgTarget){
37400             case 'qtip':
37401                 obj.el.dom.qtip = msg;
37402                 obj.el.dom.qclass = 'x-form-invalid-tip';
37403                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37404                     Roo.QuickTips.enable();
37405                 }
37406                 break;
37407             case 'title':
37408                 this.el.dom.title = msg;
37409                 break;
37410             case 'under':
37411                 if(!this.errorEl){
37412                     var elp = this.el.findParent('.x-form-element', 5, true);
37413                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37414                     this.errorEl.setWidth(elp.getWidth(true)-20);
37415                 }
37416                 this.errorEl.update(msg);
37417                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37418                 break;
37419             case 'side':
37420                 if(!this.errorIcon){
37421                     var elp = this.el.findParent('.x-form-element', 5, true);
37422                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37423                 }
37424                 this.alignErrorIcon();
37425                 this.errorIcon.dom.qtip = msg;
37426                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37427                 this.errorIcon.show();
37428                 this.on('resize', this.alignErrorIcon, this);
37429                 break;
37430             default:
37431                 var t = Roo.getDom(this.msgTarget);
37432                 t.innerHTML = msg;
37433                 t.style.display = this.msgDisplay;
37434                 break;
37435         }
37436         this.fireEvent('invalid', this, msg);
37437     },
37438
37439     // private
37440     alignErrorIcon : function(){
37441         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37442     },
37443
37444     /**
37445      * Clear any invalid styles/messages for this field
37446      */
37447     clearInvalid : function(){
37448         if(!this.rendered || this.preventMark){ // not rendered
37449             return;
37450         }
37451         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37452         
37453         obj.el.removeClass(this.invalidClass);
37454         switch(this.msgTarget){
37455             case 'qtip':
37456                 obj.el.dom.qtip = '';
37457                 break;
37458             case 'title':
37459                 this.el.dom.title = '';
37460                 break;
37461             case 'under':
37462                 if(this.errorEl){
37463                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37464                 }
37465                 break;
37466             case 'side':
37467                 if(this.errorIcon){
37468                     this.errorIcon.dom.qtip = '';
37469                     this.errorIcon.hide();
37470                     this.un('resize', this.alignErrorIcon, this);
37471                 }
37472                 break;
37473             default:
37474                 var t = Roo.getDom(this.msgTarget);
37475                 t.innerHTML = '';
37476                 t.style.display = 'none';
37477                 break;
37478         }
37479         this.fireEvent('valid', this);
37480     },
37481
37482     /**
37483      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37484      * @return {Mixed} value The field value
37485      */
37486     getRawValue : function(){
37487         var v = this.el.getValue();
37488         
37489         return v;
37490     },
37491
37492     /**
37493      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37494      * @return {Mixed} value The field value
37495      */
37496     getValue : function(){
37497         var v = this.el.getValue();
37498          
37499         return v;
37500     },
37501
37502     /**
37503      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37504      * @param {Mixed} value The value to set
37505      */
37506     setRawValue : function(v){
37507         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37508     },
37509
37510     /**
37511      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37512      * @param {Mixed} value The value to set
37513      */
37514     setValue : function(v){
37515         this.value = v;
37516         if(this.rendered){
37517             this.el.dom.value = (v === null || v === undefined ? '' : v);
37518              this.validate();
37519         }
37520     },
37521
37522     adjustSize : function(w, h){
37523         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37524         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37525         return s;
37526     },
37527
37528     adjustWidth : function(tag, w){
37529         tag = tag.toLowerCase();
37530         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37531             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37532                 if(tag == 'input'){
37533                     return w + 2;
37534                 }
37535                 if(tag == 'textarea'){
37536                     return w-2;
37537                 }
37538             }else if(Roo.isOpera){
37539                 if(tag == 'input'){
37540                     return w + 2;
37541                 }
37542                 if(tag == 'textarea'){
37543                     return w-2;
37544                 }
37545             }
37546         }
37547         return w;
37548     }
37549 });
37550
37551
37552 // anything other than normal should be considered experimental
37553 Roo.form.Field.msgFx = {
37554     normal : {
37555         show: function(msgEl, f){
37556             msgEl.setDisplayed('block');
37557         },
37558
37559         hide : function(msgEl, f){
37560             msgEl.setDisplayed(false).update('');
37561         }
37562     },
37563
37564     slide : {
37565         show: function(msgEl, f){
37566             msgEl.slideIn('t', {stopFx:true});
37567         },
37568
37569         hide : function(msgEl, f){
37570             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37571         }
37572     },
37573
37574     slideRight : {
37575         show: function(msgEl, f){
37576             msgEl.fixDisplay();
37577             msgEl.alignTo(f.el, 'tl-tr');
37578             msgEl.slideIn('l', {stopFx:true});
37579         },
37580
37581         hide : function(msgEl, f){
37582             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37583         }
37584     }
37585 };/*
37586  * Based on:
37587  * Ext JS Library 1.1.1
37588  * Copyright(c) 2006-2007, Ext JS, LLC.
37589  *
37590  * Originally Released Under LGPL - original licence link has changed is not relivant.
37591  *
37592  * Fork - LGPL
37593  * <script type="text/javascript">
37594  */
37595  
37596
37597 /**
37598  * @class Roo.form.TextField
37599  * @extends Roo.form.Field
37600  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37601  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37602  * @constructor
37603  * Creates a new TextField
37604  * @param {Object} config Configuration options
37605  */
37606 Roo.form.TextField = function(config){
37607     Roo.form.TextField.superclass.constructor.call(this, config);
37608     this.addEvents({
37609         /**
37610          * @event autosize
37611          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37612          * according to the default logic, but this event provides a hook for the developer to apply additional
37613          * logic at runtime to resize the field if needed.
37614              * @param {Roo.form.Field} this This text field
37615              * @param {Number} width The new field width
37616              */
37617         autosize : true
37618     });
37619 };
37620
37621 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37622     /**
37623      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37624      */
37625     grow : false,
37626     /**
37627      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37628      */
37629     growMin : 30,
37630     /**
37631      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37632      */
37633     growMax : 800,
37634     /**
37635      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37636      */
37637     vtype : null,
37638     /**
37639      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37640      */
37641     maskRe : null,
37642     /**
37643      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37644      */
37645     disableKeyFilter : false,
37646     /**
37647      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37648      */
37649     allowBlank : true,
37650     /**
37651      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37652      */
37653     minLength : 0,
37654     /**
37655      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37656      */
37657     maxLength : Number.MAX_VALUE,
37658     /**
37659      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37660      */
37661     minLengthText : "The minimum length for this field is {0}",
37662     /**
37663      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37664      */
37665     maxLengthText : "The maximum length for this field is {0}",
37666     /**
37667      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37668      */
37669     selectOnFocus : false,
37670     /**
37671      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37672      */
37673     blankText : "This field is required",
37674     /**
37675      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37676      * If available, this function will be called only after the basic validators all return true, and will be passed the
37677      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37678      */
37679     validator : null,
37680     /**
37681      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37682      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37683      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37684      */
37685     regex : null,
37686     /**
37687      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37688      */
37689     regexText : "",
37690     /**
37691      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37692      */
37693     emptyText : null,
37694    
37695
37696     // private
37697     initEvents : function()
37698     {
37699         if (this.emptyText) {
37700             this.el.attr('placeholder', this.emptyText);
37701         }
37702         
37703         Roo.form.TextField.superclass.initEvents.call(this);
37704         if(this.validationEvent == 'keyup'){
37705             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37706             this.el.on('keyup', this.filterValidation, this);
37707         }
37708         else if(this.validationEvent !== false){
37709             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37710         }
37711         
37712         if(this.selectOnFocus){
37713             this.on("focus", this.preFocus, this);
37714             
37715         }
37716         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37717             this.el.on("keypress", this.filterKeys, this);
37718         }
37719         if(this.grow){
37720             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37721             this.el.on("click", this.autoSize,  this);
37722         }
37723         if(this.el.is('input[type=password]') && Roo.isSafari){
37724             this.el.on('keydown', this.SafariOnKeyDown, this);
37725         }
37726     },
37727
37728     processValue : function(value){
37729         if(this.stripCharsRe){
37730             var newValue = value.replace(this.stripCharsRe, '');
37731             if(newValue !== value){
37732                 this.setRawValue(newValue);
37733                 return newValue;
37734             }
37735         }
37736         return value;
37737     },
37738
37739     filterValidation : function(e){
37740         if(!e.isNavKeyPress()){
37741             this.validationTask.delay(this.validationDelay);
37742         }
37743     },
37744
37745     // private
37746     onKeyUp : function(e){
37747         if(!e.isNavKeyPress()){
37748             this.autoSize();
37749         }
37750     },
37751
37752     /**
37753      * Resets the current field value to the originally-loaded value and clears any validation messages.
37754      *  
37755      */
37756     reset : function(){
37757         Roo.form.TextField.superclass.reset.call(this);
37758        
37759     },
37760
37761     
37762     // private
37763     preFocus : function(){
37764         
37765         if(this.selectOnFocus){
37766             this.el.dom.select();
37767         }
37768     },
37769
37770     
37771     // private
37772     filterKeys : function(e){
37773         var k = e.getKey();
37774         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37775             return;
37776         }
37777         var c = e.getCharCode(), cc = String.fromCharCode(c);
37778         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37779             return;
37780         }
37781         if(!this.maskRe.test(cc)){
37782             e.stopEvent();
37783         }
37784     },
37785
37786     setValue : function(v){
37787         
37788         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37789         
37790         this.autoSize();
37791     },
37792
37793     /**
37794      * Validates a value according to the field's validation rules and marks the field as invalid
37795      * if the validation fails
37796      * @param {Mixed} value The value to validate
37797      * @return {Boolean} True if the value is valid, else false
37798      */
37799     validateValue : function(value){
37800         if(value.length < 1)  { // if it's blank
37801              if(this.allowBlank){
37802                 this.clearInvalid();
37803                 return true;
37804              }else{
37805                 this.markInvalid(this.blankText);
37806                 return false;
37807              }
37808         }
37809         if(value.length < this.minLength){
37810             this.markInvalid(String.format(this.minLengthText, this.minLength));
37811             return false;
37812         }
37813         if(value.length > this.maxLength){
37814             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37815             return false;
37816         }
37817         if(this.vtype){
37818             var vt = Roo.form.VTypes;
37819             if(!vt[this.vtype](value, this)){
37820                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37821                 return false;
37822             }
37823         }
37824         if(typeof this.validator == "function"){
37825             var msg = this.validator(value);
37826             if(msg !== true){
37827                 this.markInvalid(msg);
37828                 return false;
37829             }
37830         }
37831         if(this.regex && !this.regex.test(value)){
37832             this.markInvalid(this.regexText);
37833             return false;
37834         }
37835         return true;
37836     },
37837
37838     /**
37839      * Selects text in this field
37840      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37841      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37842      */
37843     selectText : function(start, end){
37844         var v = this.getRawValue();
37845         if(v.length > 0){
37846             start = start === undefined ? 0 : start;
37847             end = end === undefined ? v.length : end;
37848             var d = this.el.dom;
37849             if(d.setSelectionRange){
37850                 d.setSelectionRange(start, end);
37851             }else if(d.createTextRange){
37852                 var range = d.createTextRange();
37853                 range.moveStart("character", start);
37854                 range.moveEnd("character", v.length-end);
37855                 range.select();
37856             }
37857         }
37858     },
37859
37860     /**
37861      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37862      * This only takes effect if grow = true, and fires the autosize event.
37863      */
37864     autoSize : function(){
37865         if(!this.grow || !this.rendered){
37866             return;
37867         }
37868         if(!this.metrics){
37869             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37870         }
37871         var el = this.el;
37872         var v = el.dom.value;
37873         var d = document.createElement('div');
37874         d.appendChild(document.createTextNode(v));
37875         v = d.innerHTML;
37876         d = null;
37877         v += "&#160;";
37878         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37879         this.el.setWidth(w);
37880         this.fireEvent("autosize", this, w);
37881     },
37882     
37883     // private
37884     SafariOnKeyDown : function(event)
37885     {
37886         // this is a workaround for a password hang bug on chrome/ webkit.
37887         
37888         var isSelectAll = false;
37889         
37890         if(this.el.dom.selectionEnd > 0){
37891             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37892         }
37893         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37894             event.preventDefault();
37895             this.setValue('');
37896             return;
37897         }
37898         
37899         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37900             
37901             event.preventDefault();
37902             // this is very hacky as keydown always get's upper case.
37903             
37904             var cc = String.fromCharCode(event.getCharCode());
37905             
37906             
37907             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37908             
37909         }
37910         
37911         
37912     }
37913 });/*
37914  * Based on:
37915  * Ext JS Library 1.1.1
37916  * Copyright(c) 2006-2007, Ext JS, LLC.
37917  *
37918  * Originally Released Under LGPL - original licence link has changed is not relivant.
37919  *
37920  * Fork - LGPL
37921  * <script type="text/javascript">
37922  */
37923  
37924 /**
37925  * @class Roo.form.Hidden
37926  * @extends Roo.form.TextField
37927  * Simple Hidden element used on forms 
37928  * 
37929  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37930  * 
37931  * @constructor
37932  * Creates a new Hidden form element.
37933  * @param {Object} config Configuration options
37934  */
37935
37936
37937
37938 // easy hidden field...
37939 Roo.form.Hidden = function(config){
37940     Roo.form.Hidden.superclass.constructor.call(this, config);
37941 };
37942   
37943 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37944     fieldLabel:      '',
37945     inputType:      'hidden',
37946     width:          50,
37947     allowBlank:     true,
37948     labelSeparator: '',
37949     hidden:         true,
37950     itemCls :       'x-form-item-display-none'
37951
37952
37953 });
37954
37955
37956 /*
37957  * Based on:
37958  * Ext JS Library 1.1.1
37959  * Copyright(c) 2006-2007, Ext JS, LLC.
37960  *
37961  * Originally Released Under LGPL - original licence link has changed is not relivant.
37962  *
37963  * Fork - LGPL
37964  * <script type="text/javascript">
37965  */
37966  
37967 /**
37968  * @class Roo.form.TriggerField
37969  * @extends Roo.form.TextField
37970  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37971  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37972  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37973  * for which you can provide a custom implementation.  For example:
37974  * <pre><code>
37975 var trigger = new Roo.form.TriggerField();
37976 trigger.onTriggerClick = myTriggerFn;
37977 trigger.applyTo('my-field');
37978 </code></pre>
37979  *
37980  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37981  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37982  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37983  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37984  * @constructor
37985  * Create a new TriggerField.
37986  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37987  * to the base TextField)
37988  */
37989 Roo.form.TriggerField = function(config){
37990     this.mimicing = false;
37991     Roo.form.TriggerField.superclass.constructor.call(this, config);
37992 };
37993
37994 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37995     /**
37996      * @cfg {String} triggerClass A CSS class to apply to the trigger
37997      */
37998     /**
37999      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38000      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38001      */
38002     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38003     /**
38004      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38005      */
38006     hideTrigger:false,
38007
38008     /** @cfg {Boolean} grow @hide */
38009     /** @cfg {Number} growMin @hide */
38010     /** @cfg {Number} growMax @hide */
38011
38012     /**
38013      * @hide 
38014      * @method
38015      */
38016     autoSize: Roo.emptyFn,
38017     // private
38018     monitorTab : true,
38019     // private
38020     deferHeight : true,
38021
38022     
38023     actionMode : 'wrap',
38024     // private
38025     onResize : function(w, h){
38026         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38027         if(typeof w == 'number'){
38028             var x = w - this.trigger.getWidth();
38029             this.el.setWidth(this.adjustWidth('input', x));
38030             this.trigger.setStyle('left', x+'px');
38031         }
38032     },
38033
38034     // private
38035     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38036
38037     // private
38038     getResizeEl : function(){
38039         return this.wrap;
38040     },
38041
38042     // private
38043     getPositionEl : function(){
38044         return this.wrap;
38045     },
38046
38047     // private
38048     alignErrorIcon : function(){
38049         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38050     },
38051
38052     // private
38053     onRender : function(ct, position){
38054         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38055         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38056         this.trigger = this.wrap.createChild(this.triggerConfig ||
38057                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38058         if(this.hideTrigger){
38059             this.trigger.setDisplayed(false);
38060         }
38061         this.initTrigger();
38062         if(!this.width){
38063             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38064         }
38065     },
38066
38067     // private
38068     initTrigger : function(){
38069         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38070         this.trigger.addClassOnOver('x-form-trigger-over');
38071         this.trigger.addClassOnClick('x-form-trigger-click');
38072     },
38073
38074     // private
38075     onDestroy : function(){
38076         if(this.trigger){
38077             this.trigger.removeAllListeners();
38078             this.trigger.remove();
38079         }
38080         if(this.wrap){
38081             this.wrap.remove();
38082         }
38083         Roo.form.TriggerField.superclass.onDestroy.call(this);
38084     },
38085
38086     // private
38087     onFocus : function(){
38088         Roo.form.TriggerField.superclass.onFocus.call(this);
38089         if(!this.mimicing){
38090             this.wrap.addClass('x-trigger-wrap-focus');
38091             this.mimicing = true;
38092             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38093             if(this.monitorTab){
38094                 this.el.on("keydown", this.checkTab, this);
38095             }
38096         }
38097     },
38098
38099     // private
38100     checkTab : function(e){
38101         if(e.getKey() == e.TAB){
38102             this.triggerBlur();
38103         }
38104     },
38105
38106     // private
38107     onBlur : function(){
38108         // do nothing
38109     },
38110
38111     // private
38112     mimicBlur : function(e, t){
38113         if(!this.wrap.contains(t) && this.validateBlur()){
38114             this.triggerBlur();
38115         }
38116     },
38117
38118     // private
38119     triggerBlur : function(){
38120         this.mimicing = false;
38121         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38122         if(this.monitorTab){
38123             this.el.un("keydown", this.checkTab, this);
38124         }
38125         this.wrap.removeClass('x-trigger-wrap-focus');
38126         Roo.form.TriggerField.superclass.onBlur.call(this);
38127     },
38128
38129     // private
38130     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38131     validateBlur : function(e, t){
38132         return true;
38133     },
38134
38135     // private
38136     onDisable : function(){
38137         Roo.form.TriggerField.superclass.onDisable.call(this);
38138         if(this.wrap){
38139             this.wrap.addClass('x-item-disabled');
38140         }
38141     },
38142
38143     // private
38144     onEnable : function(){
38145         Roo.form.TriggerField.superclass.onEnable.call(this);
38146         if(this.wrap){
38147             this.wrap.removeClass('x-item-disabled');
38148         }
38149     },
38150
38151     // private
38152     onShow : function(){
38153         var ae = this.getActionEl();
38154         
38155         if(ae){
38156             ae.dom.style.display = '';
38157             ae.dom.style.visibility = 'visible';
38158         }
38159     },
38160
38161     // private
38162     
38163     onHide : function(){
38164         var ae = this.getActionEl();
38165         ae.dom.style.display = 'none';
38166     },
38167
38168     /**
38169      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38170      * by an implementing function.
38171      * @method
38172      * @param {EventObject} e
38173      */
38174     onTriggerClick : Roo.emptyFn
38175 });
38176
38177 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38178 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38179 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38180 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38181     initComponent : function(){
38182         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38183
38184         this.triggerConfig = {
38185             tag:'span', cls:'x-form-twin-triggers', cn:[
38186             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38187             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38188         ]};
38189     },
38190
38191     getTrigger : function(index){
38192         return this.triggers[index];
38193     },
38194
38195     initTrigger : function(){
38196         var ts = this.trigger.select('.x-form-trigger', true);
38197         this.wrap.setStyle('overflow', 'hidden');
38198         var triggerField = this;
38199         ts.each(function(t, all, index){
38200             t.hide = function(){
38201                 var w = triggerField.wrap.getWidth();
38202                 this.dom.style.display = 'none';
38203                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38204             };
38205             t.show = function(){
38206                 var w = triggerField.wrap.getWidth();
38207                 this.dom.style.display = '';
38208                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38209             };
38210             var triggerIndex = 'Trigger'+(index+1);
38211
38212             if(this['hide'+triggerIndex]){
38213                 t.dom.style.display = 'none';
38214             }
38215             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38216             t.addClassOnOver('x-form-trigger-over');
38217             t.addClassOnClick('x-form-trigger-click');
38218         }, this);
38219         this.triggers = ts.elements;
38220     },
38221
38222     onTrigger1Click : Roo.emptyFn,
38223     onTrigger2Click : Roo.emptyFn
38224 });/*
38225  * Based on:
38226  * Ext JS Library 1.1.1
38227  * Copyright(c) 2006-2007, Ext JS, LLC.
38228  *
38229  * Originally Released Under LGPL - original licence link has changed is not relivant.
38230  *
38231  * Fork - LGPL
38232  * <script type="text/javascript">
38233  */
38234  
38235 /**
38236  * @class Roo.form.TextArea
38237  * @extends Roo.form.TextField
38238  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38239  * support for auto-sizing.
38240  * @constructor
38241  * Creates a new TextArea
38242  * @param {Object} config Configuration options
38243  */
38244 Roo.form.TextArea = function(config){
38245     Roo.form.TextArea.superclass.constructor.call(this, config);
38246     // these are provided exchanges for backwards compat
38247     // minHeight/maxHeight were replaced by growMin/growMax to be
38248     // compatible with TextField growing config values
38249     if(this.minHeight !== undefined){
38250         this.growMin = this.minHeight;
38251     }
38252     if(this.maxHeight !== undefined){
38253         this.growMax = this.maxHeight;
38254     }
38255 };
38256
38257 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38258     /**
38259      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38260      */
38261     growMin : 60,
38262     /**
38263      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38264      */
38265     growMax: 1000,
38266     /**
38267      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38268      * in the field (equivalent to setting overflow: hidden, defaults to false)
38269      */
38270     preventScrollbars: false,
38271     /**
38272      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38273      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38274      */
38275
38276     // private
38277     onRender : function(ct, position){
38278         if(!this.el){
38279             this.defaultAutoCreate = {
38280                 tag: "textarea",
38281                 style:"width:300px;height:60px;",
38282                 autocomplete: "new-password"
38283             };
38284         }
38285         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38286         if(this.grow){
38287             this.textSizeEl = Roo.DomHelper.append(document.body, {
38288                 tag: "pre", cls: "x-form-grow-sizer"
38289             });
38290             if(this.preventScrollbars){
38291                 this.el.setStyle("overflow", "hidden");
38292             }
38293             this.el.setHeight(this.growMin);
38294         }
38295     },
38296
38297     onDestroy : function(){
38298         if(this.textSizeEl){
38299             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38300         }
38301         Roo.form.TextArea.superclass.onDestroy.call(this);
38302     },
38303
38304     // private
38305     onKeyUp : function(e){
38306         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38307             this.autoSize();
38308         }
38309     },
38310
38311     /**
38312      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38313      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38314      */
38315     autoSize : function(){
38316         if(!this.grow || !this.textSizeEl){
38317             return;
38318         }
38319         var el = this.el;
38320         var v = el.dom.value;
38321         var ts = this.textSizeEl;
38322
38323         ts.innerHTML = '';
38324         ts.appendChild(document.createTextNode(v));
38325         v = ts.innerHTML;
38326
38327         Roo.fly(ts).setWidth(this.el.getWidth());
38328         if(v.length < 1){
38329             v = "&#160;&#160;";
38330         }else{
38331             if(Roo.isIE){
38332                 v = v.replace(/\n/g, '<p>&#160;</p>');
38333             }
38334             v += "&#160;\n&#160;";
38335         }
38336         ts.innerHTML = v;
38337         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38338         if(h != this.lastHeight){
38339             this.lastHeight = h;
38340             this.el.setHeight(h);
38341             this.fireEvent("autosize", this, h);
38342         }
38343     }
38344 });/*
38345  * Based on:
38346  * Ext JS Library 1.1.1
38347  * Copyright(c) 2006-2007, Ext JS, LLC.
38348  *
38349  * Originally Released Under LGPL - original licence link has changed is not relivant.
38350  *
38351  * Fork - LGPL
38352  * <script type="text/javascript">
38353  */
38354  
38355
38356 /**
38357  * @class Roo.form.NumberField
38358  * @extends Roo.form.TextField
38359  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38360  * @constructor
38361  * Creates a new NumberField
38362  * @param {Object} config Configuration options
38363  */
38364 Roo.form.NumberField = function(config){
38365     Roo.form.NumberField.superclass.constructor.call(this, config);
38366 };
38367
38368 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38369     /**
38370      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38371      */
38372     fieldClass: "x-form-field x-form-num-field",
38373     /**
38374      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38375      */
38376     allowDecimals : true,
38377     /**
38378      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38379      */
38380     decimalSeparator : ".",
38381     /**
38382      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38383      */
38384     decimalPrecision : 2,
38385     /**
38386      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38387      */
38388     allowNegative : true,
38389     /**
38390      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38391      */
38392     minValue : Number.NEGATIVE_INFINITY,
38393     /**
38394      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38395      */
38396     maxValue : Number.MAX_VALUE,
38397     /**
38398      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38399      */
38400     minText : "The minimum value for this field is {0}",
38401     /**
38402      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38403      */
38404     maxText : "The maximum value for this field is {0}",
38405     /**
38406      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38407      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38408      */
38409     nanText : "{0} is not a valid number",
38410
38411     // private
38412     initEvents : function(){
38413         Roo.form.NumberField.superclass.initEvents.call(this);
38414         var allowed = "0123456789";
38415         if(this.allowDecimals){
38416             allowed += this.decimalSeparator;
38417         }
38418         if(this.allowNegative){
38419             allowed += "-";
38420         }
38421         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38422         var keyPress = function(e){
38423             var k = e.getKey();
38424             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38425                 return;
38426             }
38427             var c = e.getCharCode();
38428             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38429                 e.stopEvent();
38430             }
38431         };
38432         this.el.on("keypress", keyPress, this);
38433     },
38434
38435     // private
38436     validateValue : function(value){
38437         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38438             return false;
38439         }
38440         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38441              return true;
38442         }
38443         var num = this.parseValue(value);
38444         if(isNaN(num)){
38445             this.markInvalid(String.format(this.nanText, value));
38446             return false;
38447         }
38448         if(num < this.minValue){
38449             this.markInvalid(String.format(this.minText, this.minValue));
38450             return false;
38451         }
38452         if(num > this.maxValue){
38453             this.markInvalid(String.format(this.maxText, this.maxValue));
38454             return false;
38455         }
38456         return true;
38457     },
38458
38459     getValue : function(){
38460         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38461     },
38462
38463     // private
38464     parseValue : function(value){
38465         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38466         return isNaN(value) ? '' : value;
38467     },
38468
38469     // private
38470     fixPrecision : function(value){
38471         var nan = isNaN(value);
38472         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38473             return nan ? '' : value;
38474         }
38475         return parseFloat(value).toFixed(this.decimalPrecision);
38476     },
38477
38478     setValue : function(v){
38479         v = this.fixPrecision(v);
38480         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38481     },
38482
38483     // private
38484     decimalPrecisionFcn : function(v){
38485         return Math.floor(v);
38486     },
38487
38488     beforeBlur : function(){
38489         var v = this.parseValue(this.getRawValue());
38490         if(v){
38491             this.setValue(v);
38492         }
38493     }
38494 });/*
38495  * Based on:
38496  * Ext JS Library 1.1.1
38497  * Copyright(c) 2006-2007, Ext JS, LLC.
38498  *
38499  * Originally Released Under LGPL - original licence link has changed is not relivant.
38500  *
38501  * Fork - LGPL
38502  * <script type="text/javascript">
38503  */
38504  
38505 /**
38506  * @class Roo.form.DateField
38507  * @extends Roo.form.TriggerField
38508  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38509 * @constructor
38510 * Create a new DateField
38511 * @param {Object} config
38512  */
38513 Roo.form.DateField = function(config){
38514     Roo.form.DateField.superclass.constructor.call(this, config);
38515     
38516       this.addEvents({
38517          
38518         /**
38519          * @event select
38520          * Fires when a date is selected
38521              * @param {Roo.form.DateField} combo This combo box
38522              * @param {Date} date The date selected
38523              */
38524         'select' : true
38525          
38526     });
38527     
38528     
38529     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38530     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38531     this.ddMatch = null;
38532     if(this.disabledDates){
38533         var dd = this.disabledDates;
38534         var re = "(?:";
38535         for(var i = 0; i < dd.length; i++){
38536             re += dd[i];
38537             if(i != dd.length-1) re += "|";
38538         }
38539         this.ddMatch = new RegExp(re + ")");
38540     }
38541 };
38542
38543 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38544     /**
38545      * @cfg {String} format
38546      * The default date format string which can be overriden for localization support.  The format must be
38547      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38548      */
38549     format : "m/d/y",
38550     /**
38551      * @cfg {String} altFormats
38552      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38553      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38554      */
38555     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38556     /**
38557      * @cfg {Array} disabledDays
38558      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38559      */
38560     disabledDays : null,
38561     /**
38562      * @cfg {String} disabledDaysText
38563      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38564      */
38565     disabledDaysText : "Disabled",
38566     /**
38567      * @cfg {Array} disabledDates
38568      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38569      * expression so they are very powerful. Some examples:
38570      * <ul>
38571      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38572      * <li>["03/08", "09/16"] would disable those days for every year</li>
38573      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38574      * <li>["03/../2006"] would disable every day in March 2006</li>
38575      * <li>["^03"] would disable every day in every March</li>
38576      * </ul>
38577      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38578      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38579      */
38580     disabledDates : null,
38581     /**
38582      * @cfg {String} disabledDatesText
38583      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38584      */
38585     disabledDatesText : "Disabled",
38586     /**
38587      * @cfg {Date/String} minValue
38588      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38589      * valid format (defaults to null).
38590      */
38591     minValue : null,
38592     /**
38593      * @cfg {Date/String} maxValue
38594      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38595      * valid format (defaults to null).
38596      */
38597     maxValue : null,
38598     /**
38599      * @cfg {String} minText
38600      * The error text to display when the date in the cell is before minValue (defaults to
38601      * 'The date in this field must be after {minValue}').
38602      */
38603     minText : "The date in this field must be equal to or after {0}",
38604     /**
38605      * @cfg {String} maxText
38606      * The error text to display when the date in the cell is after maxValue (defaults to
38607      * 'The date in this field must be before {maxValue}').
38608      */
38609     maxText : "The date in this field must be equal to or before {0}",
38610     /**
38611      * @cfg {String} invalidText
38612      * The error text to display when the date in the field is invalid (defaults to
38613      * '{value} is not a valid date - it must be in the format {format}').
38614      */
38615     invalidText : "{0} is not a valid date - it must be in the format {1}",
38616     /**
38617      * @cfg {String} triggerClass
38618      * An additional CSS class used to style the trigger button.  The trigger will always get the
38619      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38620      * which displays a calendar icon).
38621      */
38622     triggerClass : 'x-form-date-trigger',
38623     
38624
38625     /**
38626      * @cfg {Boolean} useIso
38627      * if enabled, then the date field will use a hidden field to store the 
38628      * real value as iso formated date. default (false)
38629      */ 
38630     useIso : false,
38631     /**
38632      * @cfg {String/Object} autoCreate
38633      * A DomHelper element spec, or true for a default element spec (defaults to
38634      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38635      */ 
38636     // private
38637     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38638     
38639     // private
38640     hiddenField: false,
38641     
38642     onRender : function(ct, position)
38643     {
38644         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38645         if (this.useIso) {
38646             //this.el.dom.removeAttribute('name'); 
38647             Roo.log("Changing name?");
38648             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38649             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38650                     'before', true);
38651             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38652             // prevent input submission
38653             this.hiddenName = this.name;
38654         }
38655             
38656             
38657     },
38658     
38659     // private
38660     validateValue : function(value)
38661     {
38662         value = this.formatDate(value);
38663         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38664             Roo.log('super failed');
38665             return false;
38666         }
38667         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38668              return true;
38669         }
38670         var svalue = value;
38671         value = this.parseDate(value);
38672         if(!value){
38673             Roo.log('parse date failed' + svalue);
38674             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38675             return false;
38676         }
38677         var time = value.getTime();
38678         if(this.minValue && time < this.minValue.getTime()){
38679             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38680             return false;
38681         }
38682         if(this.maxValue && time > this.maxValue.getTime()){
38683             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38684             return false;
38685         }
38686         if(this.disabledDays){
38687             var day = value.getDay();
38688             for(var i = 0; i < this.disabledDays.length; i++) {
38689                 if(day === this.disabledDays[i]){
38690                     this.markInvalid(this.disabledDaysText);
38691                     return false;
38692                 }
38693             }
38694         }
38695         var fvalue = this.formatDate(value);
38696         if(this.ddMatch && this.ddMatch.test(fvalue)){
38697             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38698             return false;
38699         }
38700         return true;
38701     },
38702
38703     // private
38704     // Provides logic to override the default TriggerField.validateBlur which just returns true
38705     validateBlur : function(){
38706         return !this.menu || !this.menu.isVisible();
38707     },
38708     
38709     getName: function()
38710     {
38711         // returns hidden if it's set..
38712         if (!this.rendered) {return ''};
38713         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38714         
38715     },
38716
38717     /**
38718      * Returns the current date value of the date field.
38719      * @return {Date} The date value
38720      */
38721     getValue : function(){
38722         
38723         return  this.hiddenField ?
38724                 this.hiddenField.value :
38725                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38726     },
38727
38728     /**
38729      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38730      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38731      * (the default format used is "m/d/y").
38732      * <br />Usage:
38733      * <pre><code>
38734 //All of these calls set the same date value (May 4, 2006)
38735
38736 //Pass a date object:
38737 var dt = new Date('5/4/06');
38738 dateField.setValue(dt);
38739
38740 //Pass a date string (default format):
38741 dateField.setValue('5/4/06');
38742
38743 //Pass a date string (custom format):
38744 dateField.format = 'Y-m-d';
38745 dateField.setValue('2006-5-4');
38746 </code></pre>
38747      * @param {String/Date} date The date or valid date string
38748      */
38749     setValue : function(date){
38750         if (this.hiddenField) {
38751             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38752         }
38753         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38754         // make sure the value field is always stored as a date..
38755         this.value = this.parseDate(date);
38756         
38757         
38758     },
38759
38760     // private
38761     parseDate : function(value){
38762         if(!value || value instanceof Date){
38763             return value;
38764         }
38765         var v = Date.parseDate(value, this.format);
38766          if (!v && this.useIso) {
38767             v = Date.parseDate(value, 'Y-m-d');
38768         }
38769         if(!v && this.altFormats){
38770             if(!this.altFormatsArray){
38771                 this.altFormatsArray = this.altFormats.split("|");
38772             }
38773             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38774                 v = Date.parseDate(value, this.altFormatsArray[i]);
38775             }
38776         }
38777         return v;
38778     },
38779
38780     // private
38781     formatDate : function(date, fmt){
38782         return (!date || !(date instanceof Date)) ?
38783                date : date.dateFormat(fmt || this.format);
38784     },
38785
38786     // private
38787     menuListeners : {
38788         select: function(m, d){
38789             
38790             this.setValue(d);
38791             this.fireEvent('select', this, d);
38792         },
38793         show : function(){ // retain focus styling
38794             this.onFocus();
38795         },
38796         hide : function(){
38797             this.focus.defer(10, this);
38798             var ml = this.menuListeners;
38799             this.menu.un("select", ml.select,  this);
38800             this.menu.un("show", ml.show,  this);
38801             this.menu.un("hide", ml.hide,  this);
38802         }
38803     },
38804
38805     // private
38806     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38807     onTriggerClick : function(){
38808         if(this.disabled){
38809             return;
38810         }
38811         if(this.menu == null){
38812             this.menu = new Roo.menu.DateMenu();
38813         }
38814         Roo.apply(this.menu.picker,  {
38815             showClear: this.allowBlank,
38816             minDate : this.minValue,
38817             maxDate : this.maxValue,
38818             disabledDatesRE : this.ddMatch,
38819             disabledDatesText : this.disabledDatesText,
38820             disabledDays : this.disabledDays,
38821             disabledDaysText : this.disabledDaysText,
38822             format : this.useIso ? 'Y-m-d' : this.format,
38823             minText : String.format(this.minText, this.formatDate(this.minValue)),
38824             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38825         });
38826         this.menu.on(Roo.apply({}, this.menuListeners, {
38827             scope:this
38828         }));
38829         this.menu.picker.setValue(this.getValue() || new Date());
38830         this.menu.show(this.el, "tl-bl?");
38831     },
38832
38833     beforeBlur : function(){
38834         var v = this.parseDate(this.getRawValue());
38835         if(v){
38836             this.setValue(v);
38837         }
38838     },
38839
38840     /*@
38841      * overide
38842      * 
38843      */
38844     isDirty : function() {
38845         if(this.disabled) {
38846             return false;
38847         }
38848         
38849         if(typeof(this.startValue) === 'undefined'){
38850             return false;
38851         }
38852         
38853         return String(this.getValue()) !== String(this.startValue);
38854         
38855     }
38856 });/*
38857  * Based on:
38858  * Ext JS Library 1.1.1
38859  * Copyright(c) 2006-2007, Ext JS, LLC.
38860  *
38861  * Originally Released Under LGPL - original licence link has changed is not relivant.
38862  *
38863  * Fork - LGPL
38864  * <script type="text/javascript">
38865  */
38866  
38867 /**
38868  * @class Roo.form.MonthField
38869  * @extends Roo.form.TriggerField
38870  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38871 * @constructor
38872 * Create a new MonthField
38873 * @param {Object} config
38874  */
38875 Roo.form.MonthField = function(config){
38876     
38877     Roo.form.MonthField.superclass.constructor.call(this, config);
38878     
38879       this.addEvents({
38880          
38881         /**
38882          * @event select
38883          * Fires when a date is selected
38884              * @param {Roo.form.MonthFieeld} combo This combo box
38885              * @param {Date} date The date selected
38886              */
38887         'select' : true
38888          
38889     });
38890     
38891     
38892     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38893     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38894     this.ddMatch = null;
38895     if(this.disabledDates){
38896         var dd = this.disabledDates;
38897         var re = "(?:";
38898         for(var i = 0; i < dd.length; i++){
38899             re += dd[i];
38900             if(i != dd.length-1) re += "|";
38901         }
38902         this.ddMatch = new RegExp(re + ")");
38903     }
38904 };
38905
38906 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38907     /**
38908      * @cfg {String} format
38909      * The default date format string which can be overriden for localization support.  The format must be
38910      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38911      */
38912     format : "M Y",
38913     /**
38914      * @cfg {String} altFormats
38915      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38916      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38917      */
38918     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38919     /**
38920      * @cfg {Array} disabledDays
38921      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38922      */
38923     disabledDays : [0,1,2,3,4,5,6],
38924     /**
38925      * @cfg {String} disabledDaysText
38926      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38927      */
38928     disabledDaysText : "Disabled",
38929     /**
38930      * @cfg {Array} disabledDates
38931      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38932      * expression so they are very powerful. Some examples:
38933      * <ul>
38934      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38935      * <li>["03/08", "09/16"] would disable those days for every year</li>
38936      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38937      * <li>["03/../2006"] would disable every day in March 2006</li>
38938      * <li>["^03"] would disable every day in every March</li>
38939      * </ul>
38940      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38941      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38942      */
38943     disabledDates : null,
38944     /**
38945      * @cfg {String} disabledDatesText
38946      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38947      */
38948     disabledDatesText : "Disabled",
38949     /**
38950      * @cfg {Date/String} minValue
38951      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38952      * valid format (defaults to null).
38953      */
38954     minValue : null,
38955     /**
38956      * @cfg {Date/String} maxValue
38957      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38958      * valid format (defaults to null).
38959      */
38960     maxValue : null,
38961     /**
38962      * @cfg {String} minText
38963      * The error text to display when the date in the cell is before minValue (defaults to
38964      * 'The date in this field must be after {minValue}').
38965      */
38966     minText : "The date in this field must be equal to or after {0}",
38967     /**
38968      * @cfg {String} maxTextf
38969      * The error text to display when the date in the cell is after maxValue (defaults to
38970      * 'The date in this field must be before {maxValue}').
38971      */
38972     maxText : "The date in this field must be equal to or before {0}",
38973     /**
38974      * @cfg {String} invalidText
38975      * The error text to display when the date in the field is invalid (defaults to
38976      * '{value} is not a valid date - it must be in the format {format}').
38977      */
38978     invalidText : "{0} is not a valid date - it must be in the format {1}",
38979     /**
38980      * @cfg {String} triggerClass
38981      * An additional CSS class used to style the trigger button.  The trigger will always get the
38982      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38983      * which displays a calendar icon).
38984      */
38985     triggerClass : 'x-form-date-trigger',
38986     
38987
38988     /**
38989      * @cfg {Boolean} useIso
38990      * if enabled, then the date field will use a hidden field to store the 
38991      * real value as iso formated date. default (true)
38992      */ 
38993     useIso : true,
38994     /**
38995      * @cfg {String/Object} autoCreate
38996      * A DomHelper element spec, or true for a default element spec (defaults to
38997      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38998      */ 
38999     // private
39000     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39001     
39002     // private
39003     hiddenField: false,
39004     
39005     hideMonthPicker : false,
39006     
39007     onRender : function(ct, position)
39008     {
39009         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39010         if (this.useIso) {
39011             this.el.dom.removeAttribute('name'); 
39012             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39013                     'before', true);
39014             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39015             // prevent input submission
39016             this.hiddenName = this.name;
39017         }
39018             
39019             
39020     },
39021     
39022     // private
39023     validateValue : function(value)
39024     {
39025         value = this.formatDate(value);
39026         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39027             return false;
39028         }
39029         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39030              return true;
39031         }
39032         var svalue = value;
39033         value = this.parseDate(value);
39034         if(!value){
39035             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39036             return false;
39037         }
39038         var time = value.getTime();
39039         if(this.minValue && time < this.minValue.getTime()){
39040             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39041             return false;
39042         }
39043         if(this.maxValue && time > this.maxValue.getTime()){
39044             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39045             return false;
39046         }
39047         /*if(this.disabledDays){
39048             var day = value.getDay();
39049             for(var i = 0; i < this.disabledDays.length; i++) {
39050                 if(day === this.disabledDays[i]){
39051                     this.markInvalid(this.disabledDaysText);
39052                     return false;
39053                 }
39054             }
39055         }
39056         */
39057         var fvalue = this.formatDate(value);
39058         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39059             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39060             return false;
39061         }
39062         */
39063         return true;
39064     },
39065
39066     // private
39067     // Provides logic to override the default TriggerField.validateBlur which just returns true
39068     validateBlur : function(){
39069         return !this.menu || !this.menu.isVisible();
39070     },
39071
39072     /**
39073      * Returns the current date value of the date field.
39074      * @return {Date} The date value
39075      */
39076     getValue : function(){
39077         
39078         
39079         
39080         return  this.hiddenField ?
39081                 this.hiddenField.value :
39082                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39083     },
39084
39085     /**
39086      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39087      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39088      * (the default format used is "m/d/y").
39089      * <br />Usage:
39090      * <pre><code>
39091 //All of these calls set the same date value (May 4, 2006)
39092
39093 //Pass a date object:
39094 var dt = new Date('5/4/06');
39095 monthField.setValue(dt);
39096
39097 //Pass a date string (default format):
39098 monthField.setValue('5/4/06');
39099
39100 //Pass a date string (custom format):
39101 monthField.format = 'Y-m-d';
39102 monthField.setValue('2006-5-4');
39103 </code></pre>
39104      * @param {String/Date} date The date or valid date string
39105      */
39106     setValue : function(date){
39107         Roo.log('month setValue' + date);
39108         // can only be first of month..
39109         
39110         var val = this.parseDate(date);
39111         
39112         if (this.hiddenField) {
39113             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39114         }
39115         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39116         this.value = this.parseDate(date);
39117     },
39118
39119     // private
39120     parseDate : function(value){
39121         if(!value || value instanceof Date){
39122             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39123             return value;
39124         }
39125         var v = Date.parseDate(value, this.format);
39126         if (!v && this.useIso) {
39127             v = Date.parseDate(value, 'Y-m-d');
39128         }
39129         if (v) {
39130             // 
39131             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39132         }
39133         
39134         
39135         if(!v && this.altFormats){
39136             if(!this.altFormatsArray){
39137                 this.altFormatsArray = this.altFormats.split("|");
39138             }
39139             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39140                 v = Date.parseDate(value, this.altFormatsArray[i]);
39141             }
39142         }
39143         return v;
39144     },
39145
39146     // private
39147     formatDate : function(date, fmt){
39148         return (!date || !(date instanceof Date)) ?
39149                date : date.dateFormat(fmt || this.format);
39150     },
39151
39152     // private
39153     menuListeners : {
39154         select: function(m, d){
39155             this.setValue(d);
39156             this.fireEvent('select', this, d);
39157         },
39158         show : function(){ // retain focus styling
39159             this.onFocus();
39160         },
39161         hide : function(){
39162             this.focus.defer(10, this);
39163             var ml = this.menuListeners;
39164             this.menu.un("select", ml.select,  this);
39165             this.menu.un("show", ml.show,  this);
39166             this.menu.un("hide", ml.hide,  this);
39167         }
39168     },
39169     // private
39170     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39171     onTriggerClick : function(){
39172         if(this.disabled){
39173             return;
39174         }
39175         if(this.menu == null){
39176             this.menu = new Roo.menu.DateMenu();
39177            
39178         }
39179         
39180         Roo.apply(this.menu.picker,  {
39181             
39182             showClear: this.allowBlank,
39183             minDate : this.minValue,
39184             maxDate : this.maxValue,
39185             disabledDatesRE : this.ddMatch,
39186             disabledDatesText : this.disabledDatesText,
39187             
39188             format : this.useIso ? 'Y-m-d' : this.format,
39189             minText : String.format(this.minText, this.formatDate(this.minValue)),
39190             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39191             
39192         });
39193          this.menu.on(Roo.apply({}, this.menuListeners, {
39194             scope:this
39195         }));
39196        
39197         
39198         var m = this.menu;
39199         var p = m.picker;
39200         
39201         // hide month picker get's called when we called by 'before hide';
39202         
39203         var ignorehide = true;
39204         p.hideMonthPicker  = function(disableAnim){
39205             if (ignorehide) {
39206                 return;
39207             }
39208              if(this.monthPicker){
39209                 Roo.log("hideMonthPicker called");
39210                 if(disableAnim === true){
39211                     this.monthPicker.hide();
39212                 }else{
39213                     this.monthPicker.slideOut('t', {duration:.2});
39214                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39215                     p.fireEvent("select", this, this.value);
39216                     m.hide();
39217                 }
39218             }
39219         }
39220         
39221         Roo.log('picker set value');
39222         Roo.log(this.getValue());
39223         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39224         m.show(this.el, 'tl-bl?');
39225         ignorehide  = false;
39226         // this will trigger hideMonthPicker..
39227         
39228         
39229         // hidden the day picker
39230         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39231         
39232         
39233         
39234       
39235         
39236         p.showMonthPicker.defer(100, p);
39237     
39238         
39239        
39240     },
39241
39242     beforeBlur : function(){
39243         var v = this.parseDate(this.getRawValue());
39244         if(v){
39245             this.setValue(v);
39246         }
39247     }
39248
39249     /** @cfg {Boolean} grow @hide */
39250     /** @cfg {Number} growMin @hide */
39251     /** @cfg {Number} growMax @hide */
39252     /**
39253      * @hide
39254      * @method autoSize
39255      */
39256 });/*
39257  * Based on:
39258  * Ext JS Library 1.1.1
39259  * Copyright(c) 2006-2007, Ext JS, LLC.
39260  *
39261  * Originally Released Under LGPL - original licence link has changed is not relivant.
39262  *
39263  * Fork - LGPL
39264  * <script type="text/javascript">
39265  */
39266  
39267
39268 /**
39269  * @class Roo.form.ComboBox
39270  * @extends Roo.form.TriggerField
39271  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39272  * @constructor
39273  * Create a new ComboBox.
39274  * @param {Object} config Configuration options
39275  */
39276 Roo.form.ComboBox = function(config){
39277     Roo.form.ComboBox.superclass.constructor.call(this, config);
39278     this.addEvents({
39279         /**
39280          * @event expand
39281          * Fires when the dropdown list is expanded
39282              * @param {Roo.form.ComboBox} combo This combo box
39283              */
39284         'expand' : true,
39285         /**
39286          * @event collapse
39287          * Fires when the dropdown list is collapsed
39288              * @param {Roo.form.ComboBox} combo This combo box
39289              */
39290         'collapse' : true,
39291         /**
39292          * @event beforeselect
39293          * Fires before a list item is selected. Return false to cancel the selection.
39294              * @param {Roo.form.ComboBox} combo This combo box
39295              * @param {Roo.data.Record} record The data record returned from the underlying store
39296              * @param {Number} index The index of the selected item in the dropdown list
39297              */
39298         'beforeselect' : true,
39299         /**
39300          * @event select
39301          * Fires when a list item is selected
39302              * @param {Roo.form.ComboBox} combo This combo box
39303              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39304              * @param {Number} index The index of the selected item in the dropdown list
39305              */
39306         'select' : true,
39307         /**
39308          * @event beforequery
39309          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39310          * The event object passed has these properties:
39311              * @param {Roo.form.ComboBox} combo This combo box
39312              * @param {String} query The query
39313              * @param {Boolean} forceAll true to force "all" query
39314              * @param {Boolean} cancel true to cancel the query
39315              * @param {Object} e The query event object
39316              */
39317         'beforequery': true,
39318          /**
39319          * @event add
39320          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39321              * @param {Roo.form.ComboBox} combo This combo box
39322              */
39323         'add' : true,
39324         /**
39325          * @event edit
39326          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39327              * @param {Roo.form.ComboBox} combo This combo box
39328              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39329              */
39330         'edit' : true
39331         
39332         
39333     });
39334     if(this.transform){
39335         this.allowDomMove = false;
39336         var s = Roo.getDom(this.transform);
39337         if(!this.hiddenName){
39338             this.hiddenName = s.name;
39339         }
39340         if(!this.store){
39341             this.mode = 'local';
39342             var d = [], opts = s.options;
39343             for(var i = 0, len = opts.length;i < len; i++){
39344                 var o = opts[i];
39345                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39346                 if(o.selected) {
39347                     this.value = value;
39348                 }
39349                 d.push([value, o.text]);
39350             }
39351             this.store = new Roo.data.SimpleStore({
39352                 'id': 0,
39353                 fields: ['value', 'text'],
39354                 data : d
39355             });
39356             this.valueField = 'value';
39357             this.displayField = 'text';
39358         }
39359         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39360         if(!this.lazyRender){
39361             this.target = true;
39362             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39363             s.parentNode.removeChild(s); // remove it
39364             this.render(this.el.parentNode);
39365         }else{
39366             s.parentNode.removeChild(s); // remove it
39367         }
39368
39369     }
39370     if (this.store) {
39371         this.store = Roo.factory(this.store, Roo.data);
39372     }
39373     
39374     this.selectedIndex = -1;
39375     if(this.mode == 'local'){
39376         if(config.queryDelay === undefined){
39377             this.queryDelay = 10;
39378         }
39379         if(config.minChars === undefined){
39380             this.minChars = 0;
39381         }
39382     }
39383 };
39384
39385 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39386     /**
39387      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39388      */
39389     /**
39390      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39391      * rendering into an Roo.Editor, defaults to false)
39392      */
39393     /**
39394      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39395      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39396      */
39397     /**
39398      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39399      */
39400     /**
39401      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39402      * the dropdown list (defaults to undefined, with no header element)
39403      */
39404
39405      /**
39406      * @cfg {String/Roo.Template} tpl The template to use to render the output
39407      */
39408      
39409     // private
39410     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39411     /**
39412      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39413      */
39414     listWidth: undefined,
39415     /**
39416      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39417      * mode = 'remote' or 'text' if mode = 'local')
39418      */
39419     displayField: undefined,
39420     /**
39421      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39422      * mode = 'remote' or 'value' if mode = 'local'). 
39423      * Note: use of a valueField requires the user make a selection
39424      * in order for a value to be mapped.
39425      */
39426     valueField: undefined,
39427     
39428     
39429     /**
39430      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39431      * field's data value (defaults to the underlying DOM element's name)
39432      */
39433     hiddenName: undefined,
39434     /**
39435      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39436      */
39437     listClass: '',
39438     /**
39439      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39440      */
39441     selectedClass: 'x-combo-selected',
39442     /**
39443      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39444      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39445      * which displays a downward arrow icon).
39446      */
39447     triggerClass : 'x-form-arrow-trigger',
39448     /**
39449      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39450      */
39451     shadow:'sides',
39452     /**
39453      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39454      * anchor positions (defaults to 'tl-bl')
39455      */
39456     listAlign: 'tl-bl?',
39457     /**
39458      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39459      */
39460     maxHeight: 300,
39461     /**
39462      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39463      * query specified by the allQuery config option (defaults to 'query')
39464      */
39465     triggerAction: 'query',
39466     /**
39467      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39468      * (defaults to 4, does not apply if editable = false)
39469      */
39470     minChars : 4,
39471     /**
39472      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39473      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39474      */
39475     typeAhead: false,
39476     /**
39477      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39478      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39479      */
39480     queryDelay: 500,
39481     /**
39482      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39483      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39484      */
39485     pageSize: 0,
39486     /**
39487      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39488      * when editable = true (defaults to false)
39489      */
39490     selectOnFocus:false,
39491     /**
39492      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39493      */
39494     queryParam: 'query',
39495     /**
39496      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39497      * when mode = 'remote' (defaults to 'Loading...')
39498      */
39499     loadingText: 'Loading...',
39500     /**
39501      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39502      */
39503     resizable: false,
39504     /**
39505      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39506      */
39507     handleHeight : 8,
39508     /**
39509      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39510      * traditional select (defaults to true)
39511      */
39512     editable: true,
39513     /**
39514      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39515      */
39516     allQuery: '',
39517     /**
39518      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39519      */
39520     mode: 'remote',
39521     /**
39522      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39523      * listWidth has a higher value)
39524      */
39525     minListWidth : 70,
39526     /**
39527      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39528      * allow the user to set arbitrary text into the field (defaults to false)
39529      */
39530     forceSelection:false,
39531     /**
39532      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39533      * if typeAhead = true (defaults to 250)
39534      */
39535     typeAheadDelay : 250,
39536     /**
39537      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39538      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39539      */
39540     valueNotFoundText : undefined,
39541     /**
39542      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39543      */
39544     blockFocus : false,
39545     
39546     /**
39547      * @cfg {Boolean} disableClear Disable showing of clear button.
39548      */
39549     disableClear : false,
39550     /**
39551      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39552      */
39553     alwaysQuery : false,
39554     
39555     //private
39556     addicon : false,
39557     editicon: false,
39558     
39559     // element that contains real text value.. (when hidden is used..)
39560      
39561     // private
39562     onRender : function(ct, position){
39563         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39564         if(this.hiddenName){
39565             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39566                     'before', true);
39567             this.hiddenField.value =
39568                 this.hiddenValue !== undefined ? this.hiddenValue :
39569                 this.value !== undefined ? this.value : '';
39570
39571             // prevent input submission
39572             this.el.dom.removeAttribute('name');
39573              
39574              
39575         }
39576         if(Roo.isGecko){
39577             this.el.dom.setAttribute('autocomplete', 'off');
39578         }
39579
39580         var cls = 'x-combo-list';
39581
39582         this.list = new Roo.Layer({
39583             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39584         });
39585
39586         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39587         this.list.setWidth(lw);
39588         this.list.swallowEvent('mousewheel');
39589         this.assetHeight = 0;
39590
39591         if(this.title){
39592             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39593             this.assetHeight += this.header.getHeight();
39594         }
39595
39596         this.innerList = this.list.createChild({cls:cls+'-inner'});
39597         this.innerList.on('mouseover', this.onViewOver, this);
39598         this.innerList.on('mousemove', this.onViewMove, this);
39599         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39600         
39601         if(this.allowBlank && !this.pageSize && !this.disableClear){
39602             this.footer = this.list.createChild({cls:cls+'-ft'});
39603             this.pageTb = new Roo.Toolbar(this.footer);
39604            
39605         }
39606         if(this.pageSize){
39607             this.footer = this.list.createChild({cls:cls+'-ft'});
39608             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39609                     {pageSize: this.pageSize});
39610             
39611         }
39612         
39613         if (this.pageTb && this.allowBlank && !this.disableClear) {
39614             var _this = this;
39615             this.pageTb.add(new Roo.Toolbar.Fill(), {
39616                 cls: 'x-btn-icon x-btn-clear',
39617                 text: '&#160;',
39618                 handler: function()
39619                 {
39620                     _this.collapse();
39621                     _this.clearValue();
39622                     _this.onSelect(false, -1);
39623                 }
39624             });
39625         }
39626         if (this.footer) {
39627             this.assetHeight += this.footer.getHeight();
39628         }
39629         
39630
39631         if(!this.tpl){
39632             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39633         }
39634
39635         this.view = new Roo.View(this.innerList, this.tpl, {
39636             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39637         });
39638
39639         this.view.on('click', this.onViewClick, this);
39640
39641         this.store.on('beforeload', this.onBeforeLoad, this);
39642         this.store.on('load', this.onLoad, this);
39643         this.store.on('loadexception', this.onLoadException, this);
39644
39645         if(this.resizable){
39646             this.resizer = new Roo.Resizable(this.list,  {
39647                pinned:true, handles:'se'
39648             });
39649             this.resizer.on('resize', function(r, w, h){
39650                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39651                 this.listWidth = w;
39652                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39653                 this.restrictHeight();
39654             }, this);
39655             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39656         }
39657         if(!this.editable){
39658             this.editable = true;
39659             this.setEditable(false);
39660         }  
39661         
39662         
39663         if (typeof(this.events.add.listeners) != 'undefined') {
39664             
39665             this.addicon = this.wrap.createChild(
39666                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39667        
39668             this.addicon.on('click', function(e) {
39669                 this.fireEvent('add', this);
39670             }, this);
39671         }
39672         if (typeof(this.events.edit.listeners) != 'undefined') {
39673             
39674             this.editicon = this.wrap.createChild(
39675                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39676             if (this.addicon) {
39677                 this.editicon.setStyle('margin-left', '40px');
39678             }
39679             this.editicon.on('click', function(e) {
39680                 
39681                 // we fire even  if inothing is selected..
39682                 this.fireEvent('edit', this, this.lastData );
39683                 
39684             }, this);
39685         }
39686         
39687         
39688         
39689     },
39690
39691     // private
39692     initEvents : function(){
39693         Roo.form.ComboBox.superclass.initEvents.call(this);
39694
39695         this.keyNav = new Roo.KeyNav(this.el, {
39696             "up" : function(e){
39697                 this.inKeyMode = true;
39698                 this.selectPrev();
39699             },
39700
39701             "down" : function(e){
39702                 if(!this.isExpanded()){
39703                     this.onTriggerClick();
39704                 }else{
39705                     this.inKeyMode = true;
39706                     this.selectNext();
39707                 }
39708             },
39709
39710             "enter" : function(e){
39711                 this.onViewClick();
39712                 //return true;
39713             },
39714
39715             "esc" : function(e){
39716                 this.collapse();
39717             },
39718
39719             "tab" : function(e){
39720                 this.onViewClick(false);
39721                 this.fireEvent("specialkey", this, e);
39722                 return true;
39723             },
39724
39725             scope : this,
39726
39727             doRelay : function(foo, bar, hname){
39728                 if(hname == 'down' || this.scope.isExpanded()){
39729                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39730                 }
39731                 return true;
39732             },
39733
39734             forceKeyDown: true
39735         });
39736         this.queryDelay = Math.max(this.queryDelay || 10,
39737                 this.mode == 'local' ? 10 : 250);
39738         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39739         if(this.typeAhead){
39740             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39741         }
39742         if(this.editable !== false){
39743             this.el.on("keyup", this.onKeyUp, this);
39744         }
39745         if(this.forceSelection){
39746             this.on('blur', this.doForce, this);
39747         }
39748     },
39749
39750     onDestroy : function(){
39751         if(this.view){
39752             this.view.setStore(null);
39753             this.view.el.removeAllListeners();
39754             this.view.el.remove();
39755             this.view.purgeListeners();
39756         }
39757         if(this.list){
39758             this.list.destroy();
39759         }
39760         if(this.store){
39761             this.store.un('beforeload', this.onBeforeLoad, this);
39762             this.store.un('load', this.onLoad, this);
39763             this.store.un('loadexception', this.onLoadException, this);
39764         }
39765         Roo.form.ComboBox.superclass.onDestroy.call(this);
39766     },
39767
39768     // private
39769     fireKey : function(e){
39770         if(e.isNavKeyPress() && !this.list.isVisible()){
39771             this.fireEvent("specialkey", this, e);
39772         }
39773     },
39774
39775     // private
39776     onResize: function(w, h){
39777         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39778         
39779         if(typeof w != 'number'){
39780             // we do not handle it!?!?
39781             return;
39782         }
39783         var tw = this.trigger.getWidth();
39784         tw += this.addicon ? this.addicon.getWidth() : 0;
39785         tw += this.editicon ? this.editicon.getWidth() : 0;
39786         var x = w - tw;
39787         this.el.setWidth( this.adjustWidth('input', x));
39788             
39789         this.trigger.setStyle('left', x+'px');
39790         
39791         if(this.list && this.listWidth === undefined){
39792             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39793             this.list.setWidth(lw);
39794             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39795         }
39796         
39797     
39798         
39799     },
39800
39801     /**
39802      * Allow or prevent the user from directly editing the field text.  If false is passed,
39803      * the user will only be able to select from the items defined in the dropdown list.  This method
39804      * is the runtime equivalent of setting the 'editable' config option at config time.
39805      * @param {Boolean} value True to allow the user to directly edit the field text
39806      */
39807     setEditable : function(value){
39808         if(value == this.editable){
39809             return;
39810         }
39811         this.editable = value;
39812         if(!value){
39813             this.el.dom.setAttribute('readOnly', true);
39814             this.el.on('mousedown', this.onTriggerClick,  this);
39815             this.el.addClass('x-combo-noedit');
39816         }else{
39817             this.el.dom.setAttribute('readOnly', false);
39818             this.el.un('mousedown', this.onTriggerClick,  this);
39819             this.el.removeClass('x-combo-noedit');
39820         }
39821     },
39822
39823     // private
39824     onBeforeLoad : function(){
39825         if(!this.hasFocus){
39826             return;
39827         }
39828         this.innerList.update(this.loadingText ?
39829                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39830         this.restrictHeight();
39831         this.selectedIndex = -1;
39832     },
39833
39834     // private
39835     onLoad : function(){
39836         if(!this.hasFocus){
39837             return;
39838         }
39839         if(this.store.getCount() > 0){
39840             this.expand();
39841             this.restrictHeight();
39842             if(this.lastQuery == this.allQuery){
39843                 if(this.editable){
39844                     this.el.dom.select();
39845                 }
39846                 if(!this.selectByValue(this.value, true)){
39847                     this.select(0, true);
39848                 }
39849             }else{
39850                 this.selectNext();
39851                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39852                     this.taTask.delay(this.typeAheadDelay);
39853                 }
39854             }
39855         }else{
39856             this.onEmptyResults();
39857         }
39858         //this.el.focus();
39859     },
39860     // private
39861     onLoadException : function()
39862     {
39863         this.collapse();
39864         Roo.log(this.store.reader.jsonData);
39865         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39866             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39867         }
39868         
39869         
39870     },
39871     // private
39872     onTypeAhead : function(){
39873         if(this.store.getCount() > 0){
39874             var r = this.store.getAt(0);
39875             var newValue = r.data[this.displayField];
39876             var len = newValue.length;
39877             var selStart = this.getRawValue().length;
39878             if(selStart != len){
39879                 this.setRawValue(newValue);
39880                 this.selectText(selStart, newValue.length);
39881             }
39882         }
39883     },
39884
39885     // private
39886     onSelect : function(record, index){
39887         if(this.fireEvent('beforeselect', this, record, index) !== false){
39888             this.setFromData(index > -1 ? record.data : false);
39889             this.collapse();
39890             this.fireEvent('select', this, record, index);
39891         }
39892     },
39893
39894     /**
39895      * Returns the currently selected field value or empty string if no value is set.
39896      * @return {String} value The selected value
39897      */
39898     getValue : function(){
39899         if(this.valueField){
39900             return typeof this.value != 'undefined' ? this.value : '';
39901         }
39902         return Roo.form.ComboBox.superclass.getValue.call(this);
39903     },
39904
39905     /**
39906      * Clears any text/value currently set in the field
39907      */
39908     clearValue : function(){
39909         if(this.hiddenField){
39910             this.hiddenField.value = '';
39911         }
39912         this.value = '';
39913         this.setRawValue('');
39914         this.lastSelectionText = '';
39915         
39916     },
39917
39918     /**
39919      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39920      * will be displayed in the field.  If the value does not match the data value of an existing item,
39921      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39922      * Otherwise the field will be blank (although the value will still be set).
39923      * @param {String} value The value to match
39924      */
39925     setValue : function(v){
39926         var text = v;
39927         if(this.valueField){
39928             var r = this.findRecord(this.valueField, v);
39929             if(r){
39930                 text = r.data[this.displayField];
39931             }else if(this.valueNotFoundText !== undefined){
39932                 text = this.valueNotFoundText;
39933             }
39934         }
39935         this.lastSelectionText = text;
39936         if(this.hiddenField){
39937             this.hiddenField.value = v;
39938         }
39939         Roo.form.ComboBox.superclass.setValue.call(this, text);
39940         this.value = v;
39941     },
39942     /**
39943      * @property {Object} the last set data for the element
39944      */
39945     
39946     lastData : false,
39947     /**
39948      * Sets the value of the field based on a object which is related to the record format for the store.
39949      * @param {Object} value the value to set as. or false on reset?
39950      */
39951     setFromData : function(o){
39952         var dv = ''; // display value
39953         var vv = ''; // value value..
39954         this.lastData = o;
39955         if (this.displayField) {
39956             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39957         } else {
39958             // this is an error condition!!!
39959             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39960         }
39961         
39962         if(this.valueField){
39963             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39964         }
39965         if(this.hiddenField){
39966             this.hiddenField.value = vv;
39967             
39968             this.lastSelectionText = dv;
39969             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39970             this.value = vv;
39971             return;
39972         }
39973         // no hidden field.. - we store the value in 'value', but still display
39974         // display field!!!!
39975         this.lastSelectionText = dv;
39976         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39977         this.value = vv;
39978         
39979         
39980     },
39981     // private
39982     reset : function(){
39983         // overridden so that last data is reset..
39984         this.setValue(this.resetValue);
39985         this.clearInvalid();
39986         this.lastData = false;
39987         if (this.view) {
39988             this.view.clearSelections();
39989         }
39990     },
39991     // private
39992     findRecord : function(prop, value){
39993         var record;
39994         if(this.store.getCount() > 0){
39995             this.store.each(function(r){
39996                 if(r.data[prop] == value){
39997                     record = r;
39998                     return false;
39999                 }
40000                 return true;
40001             });
40002         }
40003         return record;
40004     },
40005     
40006     getName: function()
40007     {
40008         // returns hidden if it's set..
40009         if (!this.rendered) {return ''};
40010         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40011         
40012     },
40013     // private
40014     onViewMove : function(e, t){
40015         this.inKeyMode = false;
40016     },
40017
40018     // private
40019     onViewOver : function(e, t){
40020         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40021             return;
40022         }
40023         var item = this.view.findItemFromChild(t);
40024         if(item){
40025             var index = this.view.indexOf(item);
40026             this.select(index, false);
40027         }
40028     },
40029
40030     // private
40031     onViewClick : function(doFocus)
40032     {
40033         var index = this.view.getSelectedIndexes()[0];
40034         var r = this.store.getAt(index);
40035         if(r){
40036             this.onSelect(r, index);
40037         }
40038         if(doFocus !== false && !this.blockFocus){
40039             this.el.focus();
40040         }
40041     },
40042
40043     // private
40044     restrictHeight : function(){
40045         this.innerList.dom.style.height = '';
40046         var inner = this.innerList.dom;
40047         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40048         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40049         this.list.beginUpdate();
40050         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40051         this.list.alignTo(this.el, this.listAlign);
40052         this.list.endUpdate();
40053     },
40054
40055     // private
40056     onEmptyResults : function(){
40057         this.collapse();
40058     },
40059
40060     /**
40061      * Returns true if the dropdown list is expanded, else false.
40062      */
40063     isExpanded : function(){
40064         return this.list.isVisible();
40065     },
40066
40067     /**
40068      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40069      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40070      * @param {String} value The data value of the item to select
40071      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40072      * selected item if it is not currently in view (defaults to true)
40073      * @return {Boolean} True if the value matched an item in the list, else false
40074      */
40075     selectByValue : function(v, scrollIntoView){
40076         if(v !== undefined && v !== null){
40077             var r = this.findRecord(this.valueField || this.displayField, v);
40078             if(r){
40079                 this.select(this.store.indexOf(r), scrollIntoView);
40080                 return true;
40081             }
40082         }
40083         return false;
40084     },
40085
40086     /**
40087      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40088      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40089      * @param {Number} index The zero-based index of the list item to select
40090      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40091      * selected item if it is not currently in view (defaults to true)
40092      */
40093     select : function(index, scrollIntoView){
40094         this.selectedIndex = index;
40095         this.view.select(index);
40096         if(scrollIntoView !== false){
40097             var el = this.view.getNode(index);
40098             if(el){
40099                 this.innerList.scrollChildIntoView(el, false);
40100             }
40101         }
40102     },
40103
40104     // private
40105     selectNext : function(){
40106         var ct = this.store.getCount();
40107         if(ct > 0){
40108             if(this.selectedIndex == -1){
40109                 this.select(0);
40110             }else if(this.selectedIndex < ct-1){
40111                 this.select(this.selectedIndex+1);
40112             }
40113         }
40114     },
40115
40116     // private
40117     selectPrev : function(){
40118         var ct = this.store.getCount();
40119         if(ct > 0){
40120             if(this.selectedIndex == -1){
40121                 this.select(0);
40122             }else if(this.selectedIndex != 0){
40123                 this.select(this.selectedIndex-1);
40124             }
40125         }
40126     },
40127
40128     // private
40129     onKeyUp : function(e){
40130         if(this.editable !== false && !e.isSpecialKey()){
40131             this.lastKey = e.getKey();
40132             this.dqTask.delay(this.queryDelay);
40133         }
40134     },
40135
40136     // private
40137     validateBlur : function(){
40138         return !this.list || !this.list.isVisible();   
40139     },
40140
40141     // private
40142     initQuery : function(){
40143         this.doQuery(this.getRawValue());
40144     },
40145
40146     // private
40147     doForce : function(){
40148         if(this.el.dom.value.length > 0){
40149             this.el.dom.value =
40150                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40151              
40152         }
40153     },
40154
40155     /**
40156      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40157      * query allowing the query action to be canceled if needed.
40158      * @param {String} query The SQL query to execute
40159      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40160      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40161      * saved in the current store (defaults to false)
40162      */
40163     doQuery : function(q, forceAll){
40164         if(q === undefined || q === null){
40165             q = '';
40166         }
40167         var qe = {
40168             query: q,
40169             forceAll: forceAll,
40170             combo: this,
40171             cancel:false
40172         };
40173         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40174             return false;
40175         }
40176         q = qe.query;
40177         forceAll = qe.forceAll;
40178         if(forceAll === true || (q.length >= this.minChars)){
40179             if(this.lastQuery != q || this.alwaysQuery){
40180                 this.lastQuery = q;
40181                 if(this.mode == 'local'){
40182                     this.selectedIndex = -1;
40183                     if(forceAll){
40184                         this.store.clearFilter();
40185                     }else{
40186                         this.store.filter(this.displayField, q);
40187                     }
40188                     this.onLoad();
40189                 }else{
40190                     this.store.baseParams[this.queryParam] = q;
40191                     this.store.load({
40192                         params: this.getParams(q)
40193                     });
40194                     this.expand();
40195                 }
40196             }else{
40197                 this.selectedIndex = -1;
40198                 this.onLoad();   
40199             }
40200         }
40201     },
40202
40203     // private
40204     getParams : function(q){
40205         var p = {};
40206         //p[this.queryParam] = q;
40207         if(this.pageSize){
40208             p.start = 0;
40209             p.limit = this.pageSize;
40210         }
40211         return p;
40212     },
40213
40214     /**
40215      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40216      */
40217     collapse : function(){
40218         if(!this.isExpanded()){
40219             return;
40220         }
40221         this.list.hide();
40222         Roo.get(document).un('mousedown', this.collapseIf, this);
40223         Roo.get(document).un('mousewheel', this.collapseIf, this);
40224         if (!this.editable) {
40225             Roo.get(document).un('keydown', this.listKeyPress, this);
40226         }
40227         this.fireEvent('collapse', this);
40228     },
40229
40230     // private
40231     collapseIf : function(e){
40232         if(!e.within(this.wrap) && !e.within(this.list)){
40233             this.collapse();
40234         }
40235     },
40236
40237     /**
40238      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40239      */
40240     expand : function(){
40241         if(this.isExpanded() || !this.hasFocus){
40242             return;
40243         }
40244         this.list.alignTo(this.el, this.listAlign);
40245         this.list.show();
40246         Roo.get(document).on('mousedown', this.collapseIf, this);
40247         Roo.get(document).on('mousewheel', this.collapseIf, this);
40248         if (!this.editable) {
40249             Roo.get(document).on('keydown', this.listKeyPress, this);
40250         }
40251         
40252         this.fireEvent('expand', this);
40253     },
40254
40255     // private
40256     // Implements the default empty TriggerField.onTriggerClick function
40257     onTriggerClick : function(){
40258         if(this.disabled){
40259             return;
40260         }
40261         if(this.isExpanded()){
40262             this.collapse();
40263             if (!this.blockFocus) {
40264                 this.el.focus();
40265             }
40266             
40267         }else {
40268             this.hasFocus = true;
40269             if(this.triggerAction == 'all') {
40270                 this.doQuery(this.allQuery, true);
40271             } else {
40272                 this.doQuery(this.getRawValue());
40273             }
40274             if (!this.blockFocus) {
40275                 this.el.focus();
40276             }
40277         }
40278     },
40279     listKeyPress : function(e)
40280     {
40281         //Roo.log('listkeypress');
40282         // scroll to first matching element based on key pres..
40283         if (e.isSpecialKey()) {
40284             return false;
40285         }
40286         var k = String.fromCharCode(e.getKey()).toUpperCase();
40287         //Roo.log(k);
40288         var match  = false;
40289         var csel = this.view.getSelectedNodes();
40290         var cselitem = false;
40291         if (csel.length) {
40292             var ix = this.view.indexOf(csel[0]);
40293             cselitem  = this.store.getAt(ix);
40294             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40295                 cselitem = false;
40296             }
40297             
40298         }
40299         
40300         this.store.each(function(v) { 
40301             if (cselitem) {
40302                 // start at existing selection.
40303                 if (cselitem.id == v.id) {
40304                     cselitem = false;
40305                 }
40306                 return;
40307             }
40308                 
40309             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40310                 match = this.store.indexOf(v);
40311                 return false;
40312             }
40313         }, this);
40314         
40315         if (match === false) {
40316             return true; // no more action?
40317         }
40318         // scroll to?
40319         this.view.select(match);
40320         var sn = Roo.get(this.view.getSelectedNodes()[0])
40321         sn.scrollIntoView(sn.dom.parentNode, false);
40322     }
40323
40324     /** 
40325     * @cfg {Boolean} grow 
40326     * @hide 
40327     */
40328     /** 
40329     * @cfg {Number} growMin 
40330     * @hide 
40331     */
40332     /** 
40333     * @cfg {Number} growMax 
40334     * @hide 
40335     */
40336     /**
40337      * @hide
40338      * @method autoSize
40339      */
40340 });/*
40341  * Copyright(c) 2010-2012, Roo J Solutions Limited
40342  *
40343  * Licence LGPL
40344  *
40345  */
40346
40347 /**
40348  * @class Roo.form.ComboBoxArray
40349  * @extends Roo.form.TextField
40350  * A facebook style adder... for lists of email / people / countries  etc...
40351  * pick multiple items from a combo box, and shows each one.
40352  *
40353  *  Fred [x]  Brian [x]  [Pick another |v]
40354  *
40355  *
40356  *  For this to work: it needs various extra information
40357  *    - normal combo problay has
40358  *      name, hiddenName
40359  *    + displayField, valueField
40360  *
40361  *    For our purpose...
40362  *
40363  *
40364  *   If we change from 'extends' to wrapping...
40365  *   
40366  *  
40367  *
40368  
40369  
40370  * @constructor
40371  * Create a new ComboBoxArray.
40372  * @param {Object} config Configuration options
40373  */
40374  
40375
40376 Roo.form.ComboBoxArray = function(config)
40377 {
40378     this.addEvents({
40379         /**
40380          * @event beforeremove
40381          * Fires before remove the value from the list
40382              * @param {Roo.form.ComboBoxArray} _self This combo box array
40383              * @param {Roo.form.ComboBoxArray.Item} item removed item
40384              */
40385         'beforeremove' : true,
40386         /**
40387          * @event remove
40388          * Fires when remove the value from the list
40389              * @param {Roo.form.ComboBoxArray} _self This combo box array
40390              * @param {Roo.form.ComboBoxArray.Item} item removed item
40391              */
40392         'remove' : true
40393         
40394         
40395     });
40396     
40397     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40398     
40399     this.items = new Roo.util.MixedCollection(false);
40400     
40401     // construct the child combo...
40402     
40403     
40404     
40405     
40406    
40407     
40408 }
40409
40410  
40411 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40412
40413     /**
40414      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40415      */
40416     
40417     lastData : false,
40418     
40419     // behavies liek a hiddne field
40420     inputType:      'hidden',
40421     /**
40422      * @cfg {Number} width The width of the box that displays the selected element
40423      */ 
40424     width:          300,
40425
40426     
40427     
40428     /**
40429      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40430      */
40431     name : false,
40432     /**
40433      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40434      */
40435     hiddenName : false,
40436     
40437     
40438     // private the array of items that are displayed..
40439     items  : false,
40440     // private - the hidden field el.
40441     hiddenEl : false,
40442     // private - the filed el..
40443     el : false,
40444     
40445     //validateValue : function() { return true; }, // all values are ok!
40446     //onAddClick: function() { },
40447     
40448     onRender : function(ct, position) 
40449     {
40450         
40451         // create the standard hidden element
40452         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40453         
40454         
40455         // give fake names to child combo;
40456         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40457         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40458         
40459         this.combo = Roo.factory(this.combo, Roo.form);
40460         this.combo.onRender(ct, position);
40461         if (typeof(this.combo.width) != 'undefined') {
40462             this.combo.onResize(this.combo.width,0);
40463         }
40464         
40465         this.combo.initEvents();
40466         
40467         // assigned so form know we need to do this..
40468         this.store          = this.combo.store;
40469         this.valueField     = this.combo.valueField;
40470         this.displayField   = this.combo.displayField ;
40471         
40472         
40473         this.combo.wrap.addClass('x-cbarray-grp');
40474         
40475         var cbwrap = this.combo.wrap.createChild(
40476             {tag: 'div', cls: 'x-cbarray-cb'},
40477             this.combo.el.dom
40478         );
40479         
40480              
40481         this.hiddenEl = this.combo.wrap.createChild({
40482             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40483         });
40484         this.el = this.combo.wrap.createChild({
40485             tag: 'input',  type:'hidden' , name: this.name, value : ''
40486         });
40487          //   this.el.dom.removeAttribute("name");
40488         
40489         
40490         this.outerWrap = this.combo.wrap;
40491         this.wrap = cbwrap;
40492         
40493         this.outerWrap.setWidth(this.width);
40494         this.outerWrap.dom.removeChild(this.el.dom);
40495         
40496         this.wrap.dom.appendChild(this.el.dom);
40497         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40498         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40499         
40500         this.combo.trigger.setStyle('position','relative');
40501         this.combo.trigger.setStyle('left', '0px');
40502         this.combo.trigger.setStyle('top', '2px');
40503         
40504         this.combo.el.setStyle('vertical-align', 'text-bottom');
40505         
40506         //this.trigger.setStyle('vertical-align', 'top');
40507         
40508         // this should use the code from combo really... on('add' ....)
40509         if (this.adder) {
40510             
40511         
40512             this.adder = this.outerWrap.createChild(
40513                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40514             var _t = this;
40515             this.adder.on('click', function(e) {
40516                 _t.fireEvent('adderclick', this, e);
40517             }, _t);
40518         }
40519         //var _t = this;
40520         //this.adder.on('click', this.onAddClick, _t);
40521         
40522         
40523         this.combo.on('select', function(cb, rec, ix) {
40524             this.addItem(rec.data);
40525             
40526             cb.setValue('');
40527             cb.el.dom.value = '';
40528             //cb.lastData = rec.data;
40529             // add to list
40530             
40531         }, this);
40532         
40533         
40534     },
40535     
40536     
40537     getName: function()
40538     {
40539         // returns hidden if it's set..
40540         if (!this.rendered) {return ''};
40541         return  this.hiddenName ? this.hiddenName : this.name;
40542         
40543     },
40544     
40545     
40546     onResize: function(w, h){
40547         
40548         return;
40549         // not sure if this is needed..
40550         //this.combo.onResize(w,h);
40551         
40552         if(typeof w != 'number'){
40553             // we do not handle it!?!?
40554             return;
40555         }
40556         var tw = this.combo.trigger.getWidth();
40557         tw += this.addicon ? this.addicon.getWidth() : 0;
40558         tw += this.editicon ? this.editicon.getWidth() : 0;
40559         var x = w - tw;
40560         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40561             
40562         this.combo.trigger.setStyle('left', '0px');
40563         
40564         if(this.list && this.listWidth === undefined){
40565             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40566             this.list.setWidth(lw);
40567             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40568         }
40569         
40570     
40571         
40572     },
40573     
40574     addItem: function(rec)
40575     {
40576         var valueField = this.combo.valueField;
40577         var displayField = this.combo.displayField;
40578         if (this.items.indexOfKey(rec[valueField]) > -1) {
40579             //console.log("GOT " + rec.data.id);
40580             return;
40581         }
40582         
40583         var x = new Roo.form.ComboBoxArray.Item({
40584             //id : rec[this.idField],
40585             data : rec,
40586             displayField : displayField ,
40587             tipField : displayField ,
40588             cb : this
40589         });
40590         // use the 
40591         this.items.add(rec[valueField],x);
40592         // add it before the element..
40593         this.updateHiddenEl();
40594         x.render(this.outerWrap, this.wrap.dom);
40595         // add the image handler..
40596     },
40597     
40598     updateHiddenEl : function()
40599     {
40600         this.validate();
40601         if (!this.hiddenEl) {
40602             return;
40603         }
40604         var ar = [];
40605         var idField = this.combo.valueField;
40606         
40607         this.items.each(function(f) {
40608             ar.push(f.data[idField]);
40609            
40610         });
40611         this.hiddenEl.dom.value = ar.join(',');
40612         this.validate();
40613     },
40614     
40615     reset : function()
40616     {
40617         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40618         this.items.each(function(f) {
40619            f.remove(); 
40620         });
40621         this.el.dom.value = '';
40622         if (this.hiddenEl) {
40623             this.hiddenEl.dom.value = '';
40624         }
40625         
40626     },
40627     getValue: function()
40628     {
40629         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40630     },
40631     setValue: function(v) // not a valid action - must use addItems..
40632     {
40633          
40634         this.reset();
40635         
40636         
40637         
40638         if (this.store.isLocal && (typeof(v) == 'string')) {
40639             // then we can use the store to find the values..
40640             // comma seperated at present.. this needs to allow JSON based encoding..
40641             this.hiddenEl.value  = v;
40642             var v_ar = [];
40643             Roo.each(v.split(','), function(k) {
40644                 Roo.log("CHECK " + this.valueField + ',' + k);
40645                 var li = this.store.query(this.valueField, k);
40646                 if (!li.length) {
40647                     return;
40648                 }
40649                 var add = {};
40650                 add[this.valueField] = k;
40651                 add[this.displayField] = li.item(0).data[this.displayField];
40652                 
40653                 this.addItem(add);
40654             }, this) 
40655              
40656         }
40657         if (typeof(v) == 'object' ) {
40658             // then let's assume it's an array of objects..
40659             Roo.each(v, function(l) {
40660                 this.addItem(l);
40661             }, this);
40662              
40663         }
40664         
40665         
40666     },
40667     setFromData: function(v)
40668     {
40669         // this recieves an object, if setValues is called.
40670         this.reset();
40671         this.el.dom.value = v[this.displayField];
40672         this.hiddenEl.dom.value = v[this.valueField];
40673         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40674             return;
40675         }
40676         var kv = v[this.valueField];
40677         var dv = v[this.displayField];
40678         kv = typeof(kv) != 'string' ? '' : kv;
40679         dv = typeof(dv) != 'string' ? '' : dv;
40680         
40681         
40682         var keys = kv.split(',');
40683         var display = dv.split(',');
40684         for (var i = 0 ; i < keys.length; i++) {
40685             
40686             add = {};
40687             add[this.valueField] = keys[i];
40688             add[this.displayField] = display[i];
40689             this.addItem(add);
40690         }
40691       
40692         
40693     },
40694     
40695     /**
40696      * Validates the combox array value
40697      * @return {Boolean} True if the value is valid, else false
40698      */
40699     validate : function(){
40700         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40701             this.clearInvalid();
40702             return true;
40703         }
40704         return false;
40705     },
40706     
40707     validateValue : function(value){
40708         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40709         
40710     },
40711     
40712     /*@
40713      * overide
40714      * 
40715      */
40716     isDirty : function() {
40717         if(this.disabled) {
40718             return false;
40719         }
40720         
40721         try {
40722             var d = Roo.decode(String(this.originalValue));
40723         } catch (e) {
40724             return String(this.getValue()) !== String(this.originalValue);
40725         }
40726         
40727         var originalValue = [];
40728         
40729         for (var i = 0; i < d.length; i++){
40730             originalValue.push(d[i][this.valueField]);
40731         }
40732         
40733         return String(this.getValue()) !== String(originalValue.join(','));
40734         
40735     }
40736     
40737 });
40738
40739
40740
40741 /**
40742  * @class Roo.form.ComboBoxArray.Item
40743  * @extends Roo.BoxComponent
40744  * A selected item in the list
40745  *  Fred [x]  Brian [x]  [Pick another |v]
40746  * 
40747  * @constructor
40748  * Create a new item.
40749  * @param {Object} config Configuration options
40750  */
40751  
40752 Roo.form.ComboBoxArray.Item = function(config) {
40753     config.id = Roo.id();
40754     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40755 }
40756
40757 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40758     data : {},
40759     cb: false,
40760     displayField : false,
40761     tipField : false,
40762     
40763     
40764     defaultAutoCreate : {
40765         tag: 'div',
40766         cls: 'x-cbarray-item',
40767         cn : [ 
40768             { tag: 'div' },
40769             {
40770                 tag: 'img',
40771                 width:16,
40772                 height : 16,
40773                 src : Roo.BLANK_IMAGE_URL ,
40774                 align: 'center'
40775             }
40776         ]
40777         
40778     },
40779     
40780  
40781     onRender : function(ct, position)
40782     {
40783         Roo.form.Field.superclass.onRender.call(this, ct, position);
40784         
40785         if(!this.el){
40786             var cfg = this.getAutoCreate();
40787             this.el = ct.createChild(cfg, position);
40788         }
40789         
40790         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40791         
40792         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40793             this.cb.renderer(this.data) :
40794             String.format('{0}',this.data[this.displayField]);
40795         
40796             
40797         this.el.child('div').dom.setAttribute('qtip',
40798                         String.format('{0}',this.data[this.tipField])
40799         );
40800         
40801         this.el.child('img').on('click', this.remove, this);
40802         
40803     },
40804    
40805     remove : function()
40806     {
40807         if(this.cb.disabled){
40808             return;
40809         }
40810         
40811         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40812             this.cb.items.remove(this);
40813             this.el.child('img').un('click', this.remove, this);
40814             this.el.remove();
40815             this.cb.updateHiddenEl();
40816
40817             this.cb.fireEvent('remove', this.cb, this);
40818         }
40819         
40820     }
40821 });/*
40822  * Based on:
40823  * Ext JS Library 1.1.1
40824  * Copyright(c) 2006-2007, Ext JS, LLC.
40825  *
40826  * Originally Released Under LGPL - original licence link has changed is not relivant.
40827  *
40828  * Fork - LGPL
40829  * <script type="text/javascript">
40830  */
40831 /**
40832  * @class Roo.form.Checkbox
40833  * @extends Roo.form.Field
40834  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40835  * @constructor
40836  * Creates a new Checkbox
40837  * @param {Object} config Configuration options
40838  */
40839 Roo.form.Checkbox = function(config){
40840     Roo.form.Checkbox.superclass.constructor.call(this, config);
40841     this.addEvents({
40842         /**
40843          * @event check
40844          * Fires when the checkbox is checked or unchecked.
40845              * @param {Roo.form.Checkbox} this This checkbox
40846              * @param {Boolean} checked The new checked value
40847              */
40848         check : true
40849     });
40850 };
40851
40852 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40853     /**
40854      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40855      */
40856     focusClass : undefined,
40857     /**
40858      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40859      */
40860     fieldClass: "x-form-field",
40861     /**
40862      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40863      */
40864     checked: false,
40865     /**
40866      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40867      * {tag: "input", type: "checkbox", autocomplete: "off"})
40868      */
40869     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40870     /**
40871      * @cfg {String} boxLabel The text that appears beside the checkbox
40872      */
40873     boxLabel : "",
40874     /**
40875      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40876      */  
40877     inputValue : '1',
40878     /**
40879      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40880      */
40881      valueOff: '0', // value when not checked..
40882
40883     actionMode : 'viewEl', 
40884     //
40885     // private
40886     itemCls : 'x-menu-check-item x-form-item',
40887     groupClass : 'x-menu-group-item',
40888     inputType : 'hidden',
40889     
40890     
40891     inSetChecked: false, // check that we are not calling self...
40892     
40893     inputElement: false, // real input element?
40894     basedOn: false, // ????
40895     
40896     isFormField: true, // not sure where this is needed!!!!
40897
40898     onResize : function(){
40899         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40900         if(!this.boxLabel){
40901             this.el.alignTo(this.wrap, 'c-c');
40902         }
40903     },
40904
40905     initEvents : function(){
40906         Roo.form.Checkbox.superclass.initEvents.call(this);
40907         this.el.on("click", this.onClick,  this);
40908         this.el.on("change", this.onClick,  this);
40909     },
40910
40911
40912     getResizeEl : function(){
40913         return this.wrap;
40914     },
40915
40916     getPositionEl : function(){
40917         return this.wrap;
40918     },
40919
40920     // private
40921     onRender : function(ct, position){
40922         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40923         /*
40924         if(this.inputValue !== undefined){
40925             this.el.dom.value = this.inputValue;
40926         }
40927         */
40928         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40929         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40930         var viewEl = this.wrap.createChild({ 
40931             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40932         this.viewEl = viewEl;   
40933         this.wrap.on('click', this.onClick,  this); 
40934         
40935         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40936         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40937         
40938         
40939         
40940         if(this.boxLabel){
40941             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40942         //    viewEl.on('click', this.onClick,  this); 
40943         }
40944         //if(this.checked){
40945             this.setChecked(this.checked);
40946         //}else{
40947             //this.checked = this.el.dom;
40948         //}
40949
40950     },
40951
40952     // private
40953     initValue : Roo.emptyFn,
40954
40955     /**
40956      * Returns the checked state of the checkbox.
40957      * @return {Boolean} True if checked, else false
40958      */
40959     getValue : function(){
40960         if(this.el){
40961             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40962         }
40963         return this.valueOff;
40964         
40965     },
40966
40967         // private
40968     onClick : function(){ 
40969         if (this.disabled) {
40970             return;
40971         }
40972         this.setChecked(!this.checked);
40973
40974         //if(this.el.dom.checked != this.checked){
40975         //    this.setValue(this.el.dom.checked);
40976        // }
40977     },
40978
40979     /**
40980      * Sets the checked state of the checkbox.
40981      * On is always based on a string comparison between inputValue and the param.
40982      * @param {Boolean/String} value - the value to set 
40983      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40984      */
40985     setValue : function(v,suppressEvent){
40986         
40987         
40988         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40989         //if(this.el && this.el.dom){
40990         //    this.el.dom.checked = this.checked;
40991         //    this.el.dom.defaultChecked = this.checked;
40992         //}
40993         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40994         //this.fireEvent("check", this, this.checked);
40995     },
40996     // private..
40997     setChecked : function(state,suppressEvent)
40998     {
40999         if (this.inSetChecked) {
41000             this.checked = state;
41001             return;
41002         }
41003         
41004     
41005         if(this.wrap){
41006             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41007         }
41008         this.checked = state;
41009         if(suppressEvent !== true){
41010             this.fireEvent('check', this, state);
41011         }
41012         this.inSetChecked = true;
41013         this.el.dom.value = state ? this.inputValue : this.valueOff;
41014         this.inSetChecked = false;
41015         
41016     },
41017     // handle setting of hidden value by some other method!!?!?
41018     setFromHidden: function()
41019     {
41020         if(!this.el){
41021             return;
41022         }
41023         //console.log("SET FROM HIDDEN");
41024         //alert('setFrom hidden');
41025         this.setValue(this.el.dom.value);
41026     },
41027     
41028     onDestroy : function()
41029     {
41030         if(this.viewEl){
41031             Roo.get(this.viewEl).remove();
41032         }
41033          
41034         Roo.form.Checkbox.superclass.onDestroy.call(this);
41035     }
41036
41037 });/*
41038  * Based on:
41039  * Ext JS Library 1.1.1
41040  * Copyright(c) 2006-2007, Ext JS, LLC.
41041  *
41042  * Originally Released Under LGPL - original licence link has changed is not relivant.
41043  *
41044  * Fork - LGPL
41045  * <script type="text/javascript">
41046  */
41047  
41048 /**
41049  * @class Roo.form.Radio
41050  * @extends Roo.form.Checkbox
41051  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41052  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41053  * @constructor
41054  * Creates a new Radio
41055  * @param {Object} config Configuration options
41056  */
41057 Roo.form.Radio = function(){
41058     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41059 };
41060 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41061     inputType: 'radio',
41062
41063     /**
41064      * If this radio is part of a group, it will return the selected value
41065      * @return {String}
41066      */
41067     getGroupValue : function(){
41068         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41069     },
41070     
41071     
41072     onRender : function(ct, position){
41073         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41074         
41075         if(this.inputValue !== undefined){
41076             this.el.dom.value = this.inputValue;
41077         }
41078          
41079         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41080         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41081         //var viewEl = this.wrap.createChild({ 
41082         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41083         //this.viewEl = viewEl;   
41084         //this.wrap.on('click', this.onClick,  this); 
41085         
41086         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41087         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41088         
41089         
41090         
41091         if(this.boxLabel){
41092             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41093         //    viewEl.on('click', this.onClick,  this); 
41094         }
41095          if(this.checked){
41096             this.el.dom.checked =   'checked' ;
41097         }
41098          
41099     } 
41100     
41101     
41102 });//<script type="text/javascript">
41103
41104 /*
41105  * Based  Ext JS Library 1.1.1
41106  * Copyright(c) 2006-2007, Ext JS, LLC.
41107  * LGPL
41108  *
41109  */
41110  
41111 /**
41112  * @class Roo.HtmlEditorCore
41113  * @extends Roo.Component
41114  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41115  *
41116  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41117  */
41118
41119 Roo.HtmlEditorCore = function(config){
41120     
41121     
41122     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41123     
41124     
41125     this.addEvents({
41126         /**
41127          * @event initialize
41128          * Fires when the editor is fully initialized (including the iframe)
41129          * @param {Roo.HtmlEditorCore} this
41130          */
41131         initialize: true,
41132         /**
41133          * @event activate
41134          * Fires when the editor is first receives the focus. Any insertion must wait
41135          * until after this event.
41136          * @param {Roo.HtmlEditorCore} this
41137          */
41138         activate: true,
41139          /**
41140          * @event beforesync
41141          * Fires before the textarea is updated with content from the editor iframe. Return false
41142          * to cancel the sync.
41143          * @param {Roo.HtmlEditorCore} this
41144          * @param {String} html
41145          */
41146         beforesync: true,
41147          /**
41148          * @event beforepush
41149          * Fires before the iframe editor is updated with content from the textarea. Return false
41150          * to cancel the push.
41151          * @param {Roo.HtmlEditorCore} this
41152          * @param {String} html
41153          */
41154         beforepush: true,
41155          /**
41156          * @event sync
41157          * Fires when the textarea is updated with content from the editor iframe.
41158          * @param {Roo.HtmlEditorCore} this
41159          * @param {String} html
41160          */
41161         sync: true,
41162          /**
41163          * @event push
41164          * Fires when the iframe editor is updated with content from the textarea.
41165          * @param {Roo.HtmlEditorCore} this
41166          * @param {String} html
41167          */
41168         push: true,
41169         
41170         /**
41171          * @event editorevent
41172          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41173          * @param {Roo.HtmlEditorCore} this
41174          */
41175         editorevent: true
41176         
41177     });
41178     
41179     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41180     
41181     // defaults : white / black...
41182     this.applyBlacklists();
41183     
41184     
41185     
41186 };
41187
41188
41189 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41190
41191
41192      /**
41193      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41194      */
41195     
41196     owner : false,
41197     
41198      /**
41199      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41200      *                        Roo.resizable.
41201      */
41202     resizable : false,
41203      /**
41204      * @cfg {Number} height (in pixels)
41205      */   
41206     height: 300,
41207    /**
41208      * @cfg {Number} width (in pixels)
41209      */   
41210     width: 500,
41211     
41212     /**
41213      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41214      * 
41215      */
41216     stylesheets: false,
41217     
41218     // id of frame..
41219     frameId: false,
41220     
41221     // private properties
41222     validationEvent : false,
41223     deferHeight: true,
41224     initialized : false,
41225     activated : false,
41226     sourceEditMode : false,
41227     onFocus : Roo.emptyFn,
41228     iframePad:3,
41229     hideMode:'offsets',
41230     
41231     clearUp: true,
41232     
41233     // blacklist + whitelisted elements..
41234     black: false,
41235     white: false,
41236      
41237     
41238
41239     /**
41240      * Protected method that will not generally be called directly. It
41241      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41242      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41243      */
41244     getDocMarkup : function(){
41245         // body styles..
41246         var st = '';
41247         
41248         // inherit styels from page...?? 
41249         if (this.stylesheets === false) {
41250             
41251             Roo.get(document.head).select('style').each(function(node) {
41252                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41253             });
41254             
41255             Roo.get(document.head).select('link').each(function(node) { 
41256                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41257             });
41258             
41259         } else if (!this.stylesheets.length) {
41260                 // simple..
41261                 st = '<style type="text/css">' +
41262                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41263                    '</style>';
41264         } else { 
41265             
41266         }
41267         
41268         st +=  '<style type="text/css">' +
41269             'IMG { cursor: pointer } ' +
41270         '</style>';
41271
41272         
41273         return '<html><head>' + st  +
41274             //<style type="text/css">' +
41275             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41276             //'</style>' +
41277             ' </head><body class="roo-htmleditor-body"></body></html>';
41278     },
41279
41280     // private
41281     onRender : function(ct, position)
41282     {
41283         var _t = this;
41284         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41285         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41286         
41287         
41288         this.el.dom.style.border = '0 none';
41289         this.el.dom.setAttribute('tabIndex', -1);
41290         this.el.addClass('x-hidden hide');
41291         
41292         
41293         
41294         if(Roo.isIE){ // fix IE 1px bogus margin
41295             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41296         }
41297        
41298         
41299         this.frameId = Roo.id();
41300         
41301          
41302         
41303         var iframe = this.owner.wrap.createChild({
41304             tag: 'iframe',
41305             cls: 'form-control', // bootstrap..
41306             id: this.frameId,
41307             name: this.frameId,
41308             frameBorder : 'no',
41309             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41310         }, this.el
41311         );
41312         
41313         
41314         this.iframe = iframe.dom;
41315
41316          this.assignDocWin();
41317         
41318         this.doc.designMode = 'on';
41319        
41320         this.doc.open();
41321         this.doc.write(this.getDocMarkup());
41322         this.doc.close();
41323
41324         
41325         var task = { // must defer to wait for browser to be ready
41326             run : function(){
41327                 //console.log("run task?" + this.doc.readyState);
41328                 this.assignDocWin();
41329                 if(this.doc.body || this.doc.readyState == 'complete'){
41330                     try {
41331                         this.doc.designMode="on";
41332                     } catch (e) {
41333                         return;
41334                     }
41335                     Roo.TaskMgr.stop(task);
41336                     this.initEditor.defer(10, this);
41337                 }
41338             },
41339             interval : 10,
41340             duration: 10000,
41341             scope: this
41342         };
41343         Roo.TaskMgr.start(task);
41344
41345     },
41346
41347     // private
41348     onResize : function(w, h)
41349     {
41350          Roo.log('resize: ' +w + ',' + h );
41351         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41352         if(!this.iframe){
41353             return;
41354         }
41355         if(typeof w == 'number'){
41356             
41357             this.iframe.style.width = w + 'px';
41358         }
41359         if(typeof h == 'number'){
41360             
41361             this.iframe.style.height = h + 'px';
41362             if(this.doc){
41363                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41364             }
41365         }
41366         
41367     },
41368
41369     /**
41370      * Toggles the editor between standard and source edit mode.
41371      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41372      */
41373     toggleSourceEdit : function(sourceEditMode){
41374         
41375         this.sourceEditMode = sourceEditMode === true;
41376         
41377         if(this.sourceEditMode){
41378  
41379             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41380             
41381         }else{
41382             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41383             //this.iframe.className = '';
41384             this.deferFocus();
41385         }
41386         //this.setSize(this.owner.wrap.getSize());
41387         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41388     },
41389
41390     
41391   
41392
41393     /**
41394      * Protected method that will not generally be called directly. If you need/want
41395      * custom HTML cleanup, this is the method you should override.
41396      * @param {String} html The HTML to be cleaned
41397      * return {String} The cleaned HTML
41398      */
41399     cleanHtml : function(html){
41400         html = String(html);
41401         if(html.length > 5){
41402             if(Roo.isSafari){ // strip safari nonsense
41403                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41404             }
41405         }
41406         if(html == '&nbsp;'){
41407             html = '';
41408         }
41409         return html;
41410     },
41411
41412     /**
41413      * HTML Editor -> Textarea
41414      * Protected method that will not generally be called directly. Syncs the contents
41415      * of the editor iframe with the textarea.
41416      */
41417     syncValue : function(){
41418         if(this.initialized){
41419             var bd = (this.doc.body || this.doc.documentElement);
41420             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41421             var html = bd.innerHTML;
41422             if(Roo.isSafari){
41423                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41424                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41425                 if(m && m[1]){
41426                     html = '<div style="'+m[0]+'">' + html + '</div>';
41427                 }
41428             }
41429             html = this.cleanHtml(html);
41430             // fix up the special chars.. normaly like back quotes in word...
41431             // however we do not want to do this with chinese..
41432             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41433                 var cc = b.charCodeAt();
41434                 if (
41435                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41436                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41437                     (cc >= 0xf900 && cc < 0xfb00 )
41438                 ) {
41439                         return b;
41440                 }
41441                 return "&#"+cc+";" 
41442             });
41443             if(this.owner.fireEvent('beforesync', this, html) !== false){
41444                 this.el.dom.value = html;
41445                 this.owner.fireEvent('sync', this, html);
41446             }
41447         }
41448     },
41449
41450     /**
41451      * Protected method that will not generally be called directly. Pushes the value of the textarea
41452      * into the iframe editor.
41453      */
41454     pushValue : function(){
41455         if(this.initialized){
41456             var v = this.el.dom.value.trim();
41457             
41458 //            if(v.length < 1){
41459 //                v = '&#160;';
41460 //            }
41461             
41462             if(this.owner.fireEvent('beforepush', this, v) !== false){
41463                 var d = (this.doc.body || this.doc.documentElement);
41464                 d.innerHTML = v;
41465                 this.cleanUpPaste();
41466                 this.el.dom.value = d.innerHTML;
41467                 this.owner.fireEvent('push', this, v);
41468             }
41469         }
41470     },
41471
41472     // private
41473     deferFocus : function(){
41474         this.focus.defer(10, this);
41475     },
41476
41477     // doc'ed in Field
41478     focus : function(){
41479         if(this.win && !this.sourceEditMode){
41480             this.win.focus();
41481         }else{
41482             this.el.focus();
41483         }
41484     },
41485     
41486     assignDocWin: function()
41487     {
41488         var iframe = this.iframe;
41489         
41490          if(Roo.isIE){
41491             this.doc = iframe.contentWindow.document;
41492             this.win = iframe.contentWindow;
41493         } else {
41494 //            if (!Roo.get(this.frameId)) {
41495 //                return;
41496 //            }
41497 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41498 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41499             
41500             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41501                 return;
41502             }
41503             
41504             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41505             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41506         }
41507     },
41508     
41509     // private
41510     initEditor : function(){
41511         //console.log("INIT EDITOR");
41512         this.assignDocWin();
41513         
41514         
41515         
41516         this.doc.designMode="on";
41517         this.doc.open();
41518         this.doc.write(this.getDocMarkup());
41519         this.doc.close();
41520         
41521         var dbody = (this.doc.body || this.doc.documentElement);
41522         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41523         // this copies styles from the containing element into thsi one..
41524         // not sure why we need all of this..
41525         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41526         
41527         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41528         //ss['background-attachment'] = 'fixed'; // w3c
41529         dbody.bgProperties = 'fixed'; // ie
41530         //Roo.DomHelper.applyStyles(dbody, ss);
41531         Roo.EventManager.on(this.doc, {
41532             //'mousedown': this.onEditorEvent,
41533             'mouseup': this.onEditorEvent,
41534             'dblclick': this.onEditorEvent,
41535             'click': this.onEditorEvent,
41536             'keyup': this.onEditorEvent,
41537             buffer:100,
41538             scope: this
41539         });
41540         if(Roo.isGecko){
41541             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41542         }
41543         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41544             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41545         }
41546         this.initialized = true;
41547
41548         this.owner.fireEvent('initialize', this);
41549         this.pushValue();
41550     },
41551
41552     // private
41553     onDestroy : function(){
41554         
41555         
41556         
41557         if(this.rendered){
41558             
41559             //for (var i =0; i < this.toolbars.length;i++) {
41560             //    // fixme - ask toolbars for heights?
41561             //    this.toolbars[i].onDestroy();
41562            // }
41563             
41564             //this.wrap.dom.innerHTML = '';
41565             //this.wrap.remove();
41566         }
41567     },
41568
41569     // private
41570     onFirstFocus : function(){
41571         
41572         this.assignDocWin();
41573         
41574         
41575         this.activated = true;
41576          
41577     
41578         if(Roo.isGecko){ // prevent silly gecko errors
41579             this.win.focus();
41580             var s = this.win.getSelection();
41581             if(!s.focusNode || s.focusNode.nodeType != 3){
41582                 var r = s.getRangeAt(0);
41583                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41584                 r.collapse(true);
41585                 this.deferFocus();
41586             }
41587             try{
41588                 this.execCmd('useCSS', true);
41589                 this.execCmd('styleWithCSS', false);
41590             }catch(e){}
41591         }
41592         this.owner.fireEvent('activate', this);
41593     },
41594
41595     // private
41596     adjustFont: function(btn){
41597         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41598         //if(Roo.isSafari){ // safari
41599         //    adjust *= 2;
41600        // }
41601         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41602         if(Roo.isSafari){ // safari
41603             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41604             v =  (v < 10) ? 10 : v;
41605             v =  (v > 48) ? 48 : v;
41606             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41607             
41608         }
41609         
41610         
41611         v = Math.max(1, v+adjust);
41612         
41613         this.execCmd('FontSize', v  );
41614     },
41615
41616     onEditorEvent : function(e)
41617     {
41618         this.owner.fireEvent('editorevent', this, e);
41619       //  this.updateToolbar();
41620         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41621     },
41622
41623     insertTag : function(tg)
41624     {
41625         // could be a bit smarter... -> wrap the current selected tRoo..
41626         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41627             
41628             range = this.createRange(this.getSelection());
41629             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41630             wrappingNode.appendChild(range.extractContents());
41631             range.insertNode(wrappingNode);
41632
41633             return;
41634             
41635             
41636             
41637         }
41638         this.execCmd("formatblock",   tg);
41639         
41640     },
41641     
41642     insertText : function(txt)
41643     {
41644         
41645         
41646         var range = this.createRange();
41647         range.deleteContents();
41648                //alert(Sender.getAttribute('label'));
41649                
41650         range.insertNode(this.doc.createTextNode(txt));
41651     } ,
41652     
41653      
41654
41655     /**
41656      * Executes a Midas editor command on the editor document and performs necessary focus and
41657      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41658      * @param {String} cmd The Midas command
41659      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41660      */
41661     relayCmd : function(cmd, value){
41662         this.win.focus();
41663         this.execCmd(cmd, value);
41664         this.owner.fireEvent('editorevent', this);
41665         //this.updateToolbar();
41666         this.owner.deferFocus();
41667     },
41668
41669     /**
41670      * Executes a Midas editor command directly on the editor document.
41671      * For visual commands, you should use {@link #relayCmd} instead.
41672      * <b>This should only be called after the editor is initialized.</b>
41673      * @param {String} cmd The Midas command
41674      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41675      */
41676     execCmd : function(cmd, value){
41677         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41678         this.syncValue();
41679     },
41680  
41681  
41682    
41683     /**
41684      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41685      * to insert tRoo.
41686      * @param {String} text | dom node.. 
41687      */
41688     insertAtCursor : function(text)
41689     {
41690         
41691         
41692         
41693         if(!this.activated){
41694             return;
41695         }
41696         /*
41697         if(Roo.isIE){
41698             this.win.focus();
41699             var r = this.doc.selection.createRange();
41700             if(r){
41701                 r.collapse(true);
41702                 r.pasteHTML(text);
41703                 this.syncValue();
41704                 this.deferFocus();
41705             
41706             }
41707             return;
41708         }
41709         */
41710         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41711             this.win.focus();
41712             
41713             
41714             // from jquery ui (MIT licenced)
41715             var range, node;
41716             var win = this.win;
41717             
41718             if (win.getSelection && win.getSelection().getRangeAt) {
41719                 range = win.getSelection().getRangeAt(0);
41720                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41721                 range.insertNode(node);
41722             } else if (win.document.selection && win.document.selection.createRange) {
41723                 // no firefox support
41724                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41725                 win.document.selection.createRange().pasteHTML(txt);
41726             } else {
41727                 // no firefox support
41728                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41729                 this.execCmd('InsertHTML', txt);
41730             } 
41731             
41732             this.syncValue();
41733             
41734             this.deferFocus();
41735         }
41736     },
41737  // private
41738     mozKeyPress : function(e){
41739         if(e.ctrlKey){
41740             var c = e.getCharCode(), cmd;
41741           
41742             if(c > 0){
41743                 c = String.fromCharCode(c).toLowerCase();
41744                 switch(c){
41745                     case 'b':
41746                         cmd = 'bold';
41747                         break;
41748                     case 'i':
41749                         cmd = 'italic';
41750                         break;
41751                     
41752                     case 'u':
41753                         cmd = 'underline';
41754                         break;
41755                     
41756                     case 'v':
41757                         this.cleanUpPaste.defer(100, this);
41758                         return;
41759                         
41760                 }
41761                 if(cmd){
41762                     this.win.focus();
41763                     this.execCmd(cmd);
41764                     this.deferFocus();
41765                     e.preventDefault();
41766                 }
41767                 
41768             }
41769         }
41770     },
41771
41772     // private
41773     fixKeys : function(){ // load time branching for fastest keydown performance
41774         if(Roo.isIE){
41775             return function(e){
41776                 var k = e.getKey(), r;
41777                 if(k == e.TAB){
41778                     e.stopEvent();
41779                     r = this.doc.selection.createRange();
41780                     if(r){
41781                         r.collapse(true);
41782                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41783                         this.deferFocus();
41784                     }
41785                     return;
41786                 }
41787                 
41788                 if(k == e.ENTER){
41789                     r = this.doc.selection.createRange();
41790                     if(r){
41791                         var target = r.parentElement();
41792                         if(!target || target.tagName.toLowerCase() != 'li'){
41793                             e.stopEvent();
41794                             r.pasteHTML('<br />');
41795                             r.collapse(false);
41796                             r.select();
41797                         }
41798                     }
41799                 }
41800                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41801                     this.cleanUpPaste.defer(100, this);
41802                     return;
41803                 }
41804                 
41805                 
41806             };
41807         }else if(Roo.isOpera){
41808             return function(e){
41809                 var k = e.getKey();
41810                 if(k == e.TAB){
41811                     e.stopEvent();
41812                     this.win.focus();
41813                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41814                     this.deferFocus();
41815                 }
41816                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41817                     this.cleanUpPaste.defer(100, this);
41818                     return;
41819                 }
41820                 
41821             };
41822         }else if(Roo.isSafari){
41823             return function(e){
41824                 var k = e.getKey();
41825                 
41826                 if(k == e.TAB){
41827                     e.stopEvent();
41828                     this.execCmd('InsertText','\t');
41829                     this.deferFocus();
41830                     return;
41831                 }
41832                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41833                     this.cleanUpPaste.defer(100, this);
41834                     return;
41835                 }
41836                 
41837              };
41838         }
41839     }(),
41840     
41841     getAllAncestors: function()
41842     {
41843         var p = this.getSelectedNode();
41844         var a = [];
41845         if (!p) {
41846             a.push(p); // push blank onto stack..
41847             p = this.getParentElement();
41848         }
41849         
41850         
41851         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41852             a.push(p);
41853             p = p.parentNode;
41854         }
41855         a.push(this.doc.body);
41856         return a;
41857     },
41858     lastSel : false,
41859     lastSelNode : false,
41860     
41861     
41862     getSelection : function() 
41863     {
41864         this.assignDocWin();
41865         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41866     },
41867     
41868     getSelectedNode: function() 
41869     {
41870         // this may only work on Gecko!!!
41871         
41872         // should we cache this!!!!
41873         
41874         
41875         
41876          
41877         var range = this.createRange(this.getSelection()).cloneRange();
41878         
41879         if (Roo.isIE) {
41880             var parent = range.parentElement();
41881             while (true) {
41882                 var testRange = range.duplicate();
41883                 testRange.moveToElementText(parent);
41884                 if (testRange.inRange(range)) {
41885                     break;
41886                 }
41887                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41888                     break;
41889                 }
41890                 parent = parent.parentElement;
41891             }
41892             return parent;
41893         }
41894         
41895         // is ancestor a text element.
41896         var ac =  range.commonAncestorContainer;
41897         if (ac.nodeType == 3) {
41898             ac = ac.parentNode;
41899         }
41900         
41901         var ar = ac.childNodes;
41902          
41903         var nodes = [];
41904         var other_nodes = [];
41905         var has_other_nodes = false;
41906         for (var i=0;i<ar.length;i++) {
41907             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41908                 continue;
41909             }
41910             // fullly contained node.
41911             
41912             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41913                 nodes.push(ar[i]);
41914                 continue;
41915             }
41916             
41917             // probably selected..
41918             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41919                 other_nodes.push(ar[i]);
41920                 continue;
41921             }
41922             // outer..
41923             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41924                 continue;
41925             }
41926             
41927             
41928             has_other_nodes = true;
41929         }
41930         if (!nodes.length && other_nodes.length) {
41931             nodes= other_nodes;
41932         }
41933         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41934             return false;
41935         }
41936         
41937         return nodes[0];
41938     },
41939     createRange: function(sel)
41940     {
41941         // this has strange effects when using with 
41942         // top toolbar - not sure if it's a great idea.
41943         //this.editor.contentWindow.focus();
41944         if (typeof sel != "undefined") {
41945             try {
41946                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41947             } catch(e) {
41948                 return this.doc.createRange();
41949             }
41950         } else {
41951             return this.doc.createRange();
41952         }
41953     },
41954     getParentElement: function()
41955     {
41956         
41957         this.assignDocWin();
41958         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41959         
41960         var range = this.createRange(sel);
41961          
41962         try {
41963             var p = range.commonAncestorContainer;
41964             while (p.nodeType == 3) { // text node
41965                 p = p.parentNode;
41966             }
41967             return p;
41968         } catch (e) {
41969             return null;
41970         }
41971     
41972     },
41973     /***
41974      *
41975      * Range intersection.. the hard stuff...
41976      *  '-1' = before
41977      *  '0' = hits..
41978      *  '1' = after.
41979      *         [ -- selected range --- ]
41980      *   [fail]                        [fail]
41981      *
41982      *    basically..
41983      *      if end is before start or  hits it. fail.
41984      *      if start is after end or hits it fail.
41985      *
41986      *   if either hits (but other is outside. - then it's not 
41987      *   
41988      *    
41989      **/
41990     
41991     
41992     // @see http://www.thismuchiknow.co.uk/?p=64.
41993     rangeIntersectsNode : function(range, node)
41994     {
41995         var nodeRange = node.ownerDocument.createRange();
41996         try {
41997             nodeRange.selectNode(node);
41998         } catch (e) {
41999             nodeRange.selectNodeContents(node);
42000         }
42001     
42002         var rangeStartRange = range.cloneRange();
42003         rangeStartRange.collapse(true);
42004     
42005         var rangeEndRange = range.cloneRange();
42006         rangeEndRange.collapse(false);
42007     
42008         var nodeStartRange = nodeRange.cloneRange();
42009         nodeStartRange.collapse(true);
42010     
42011         var nodeEndRange = nodeRange.cloneRange();
42012         nodeEndRange.collapse(false);
42013     
42014         return rangeStartRange.compareBoundaryPoints(
42015                  Range.START_TO_START, nodeEndRange) == -1 &&
42016                rangeEndRange.compareBoundaryPoints(
42017                  Range.START_TO_START, nodeStartRange) == 1;
42018         
42019          
42020     },
42021     rangeCompareNode : function(range, node)
42022     {
42023         var nodeRange = node.ownerDocument.createRange();
42024         try {
42025             nodeRange.selectNode(node);
42026         } catch (e) {
42027             nodeRange.selectNodeContents(node);
42028         }
42029         
42030         
42031         range.collapse(true);
42032     
42033         nodeRange.collapse(true);
42034      
42035         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42036         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42037          
42038         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42039         
42040         var nodeIsBefore   =  ss == 1;
42041         var nodeIsAfter    = ee == -1;
42042         
42043         if (nodeIsBefore && nodeIsAfter)
42044             return 0; // outer
42045         if (!nodeIsBefore && nodeIsAfter)
42046             return 1; //right trailed.
42047         
42048         if (nodeIsBefore && !nodeIsAfter)
42049             return 2;  // left trailed.
42050         // fully contined.
42051         return 3;
42052     },
42053
42054     // private? - in a new class?
42055     cleanUpPaste :  function()
42056     {
42057         // cleans up the whole document..
42058         Roo.log('cleanuppaste');
42059         
42060         this.cleanUpChildren(this.doc.body);
42061         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42062         if (clean != this.doc.body.innerHTML) {
42063             this.doc.body.innerHTML = clean;
42064         }
42065         
42066     },
42067     
42068     cleanWordChars : function(input) {// change the chars to hex code
42069         var he = Roo.HtmlEditorCore;
42070         
42071         var output = input;
42072         Roo.each(he.swapCodes, function(sw) { 
42073             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42074             
42075             output = output.replace(swapper, sw[1]);
42076         });
42077         
42078         return output;
42079     },
42080     
42081     
42082     cleanUpChildren : function (n)
42083     {
42084         if (!n.childNodes.length) {
42085             return;
42086         }
42087         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42088            this.cleanUpChild(n.childNodes[i]);
42089         }
42090     },
42091     
42092     
42093         
42094     
42095     cleanUpChild : function (node)
42096     {
42097         var ed = this;
42098         //console.log(node);
42099         if (node.nodeName == "#text") {
42100             // clean up silly Windows -- stuff?
42101             return; 
42102         }
42103         if (node.nodeName == "#comment") {
42104             node.parentNode.removeChild(node);
42105             // clean up silly Windows -- stuff?
42106             return; 
42107         }
42108         var lcname = node.tagName.toLowerCase();
42109         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42110         // whitelist of tags..
42111         
42112         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42113             // remove node.
42114             node.parentNode.removeChild(node);
42115             return;
42116             
42117         }
42118         
42119         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42120         
42121         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42122         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42123         
42124         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42125         //    remove_keep_children = true;
42126         //}
42127         
42128         if (remove_keep_children) {
42129             this.cleanUpChildren(node);
42130             // inserts everything just before this node...
42131             while (node.childNodes.length) {
42132                 var cn = node.childNodes[0];
42133                 node.removeChild(cn);
42134                 node.parentNode.insertBefore(cn, node);
42135             }
42136             node.parentNode.removeChild(node);
42137             return;
42138         }
42139         
42140         if (!node.attributes || !node.attributes.length) {
42141             this.cleanUpChildren(node);
42142             return;
42143         }
42144         
42145         function cleanAttr(n,v)
42146         {
42147             
42148             if (v.match(/^\./) || v.match(/^\//)) {
42149                 return;
42150             }
42151             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42152                 return;
42153             }
42154             if (v.match(/^#/)) {
42155                 return;
42156             }
42157 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42158             node.removeAttribute(n);
42159             
42160         }
42161         
42162         var cwhite = this.cwhite;
42163         var cblack = this.cblack;
42164             
42165         function cleanStyle(n,v)
42166         {
42167             if (v.match(/expression/)) { //XSS?? should we even bother..
42168                 node.removeAttribute(n);
42169                 return;
42170             }
42171             
42172             var parts = v.split(/;/);
42173             var clean = [];
42174             
42175             Roo.each(parts, function(p) {
42176                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42177                 if (!p.length) {
42178                     return true;
42179                 }
42180                 var l = p.split(':').shift().replace(/\s+/g,'');
42181                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42182                 
42183                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42184 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42185                     //node.removeAttribute(n);
42186                     return true;
42187                 }
42188                 //Roo.log()
42189                 // only allow 'c whitelisted system attributes'
42190                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42191 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42192                     //node.removeAttribute(n);
42193                     return true;
42194                 }
42195                 
42196                 
42197                  
42198                 
42199                 clean.push(p);
42200                 return true;
42201             });
42202             if (clean.length) { 
42203                 node.setAttribute(n, clean.join(';'));
42204             } else {
42205                 node.removeAttribute(n);
42206             }
42207             
42208         }
42209         
42210         
42211         for (var i = node.attributes.length-1; i > -1 ; i--) {
42212             var a = node.attributes[i];
42213             //console.log(a);
42214             
42215             if (a.name.toLowerCase().substr(0,2)=='on')  {
42216                 node.removeAttribute(a.name);
42217                 continue;
42218             }
42219             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42220                 node.removeAttribute(a.name);
42221                 continue;
42222             }
42223             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42224                 cleanAttr(a.name,a.value); // fixme..
42225                 continue;
42226             }
42227             if (a.name == 'style') {
42228                 cleanStyle(a.name,a.value);
42229                 continue;
42230             }
42231             /// clean up MS crap..
42232             // tecnically this should be a list of valid class'es..
42233             
42234             
42235             if (a.name == 'class') {
42236                 if (a.value.match(/^Mso/)) {
42237                     node.className = '';
42238                 }
42239                 
42240                 if (a.value.match(/body/)) {
42241                     node.className = '';
42242                 }
42243                 continue;
42244             }
42245             
42246             // style cleanup!?
42247             // class cleanup?
42248             
42249         }
42250         
42251         
42252         this.cleanUpChildren(node);
42253         
42254         
42255     },
42256     
42257     /**
42258      * Clean up MS wordisms...
42259      */
42260     cleanWord : function(node)
42261     {
42262         
42263         
42264         if (!node) {
42265             this.cleanWord(this.doc.body);
42266             return;
42267         }
42268         if (node.nodeName == "#text") {
42269             // clean up silly Windows -- stuff?
42270             return; 
42271         }
42272         if (node.nodeName == "#comment") {
42273             node.parentNode.removeChild(node);
42274             // clean up silly Windows -- stuff?
42275             return; 
42276         }
42277         
42278         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42279             node.parentNode.removeChild(node);
42280             return;
42281         }
42282         
42283         // remove - but keep children..
42284         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42285             while (node.childNodes.length) {
42286                 var cn = node.childNodes[0];
42287                 node.removeChild(cn);
42288                 node.parentNode.insertBefore(cn, node);
42289             }
42290             node.parentNode.removeChild(node);
42291             this.iterateChildren(node, this.cleanWord);
42292             return;
42293         }
42294         // clean styles
42295         if (node.className.length) {
42296             
42297             var cn = node.className.split(/\W+/);
42298             var cna = [];
42299             Roo.each(cn, function(cls) {
42300                 if (cls.match(/Mso[a-zA-Z]+/)) {
42301                     return;
42302                 }
42303                 cna.push(cls);
42304             });
42305             node.className = cna.length ? cna.join(' ') : '';
42306             if (!cna.length) {
42307                 node.removeAttribute("class");
42308             }
42309         }
42310         
42311         if (node.hasAttribute("lang")) {
42312             node.removeAttribute("lang");
42313         }
42314         
42315         if (node.hasAttribute("style")) {
42316             
42317             var styles = node.getAttribute("style").split(";");
42318             var nstyle = [];
42319             Roo.each(styles, function(s) {
42320                 if (!s.match(/:/)) {
42321                     return;
42322                 }
42323                 var kv = s.split(":");
42324                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42325                     return;
42326                 }
42327                 // what ever is left... we allow.
42328                 nstyle.push(s);
42329             });
42330             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42331             if (!nstyle.length) {
42332                 node.removeAttribute('style');
42333             }
42334         }
42335         this.iterateChildren(node, this.cleanWord);
42336         
42337         
42338         
42339     },
42340     /**
42341      * iterateChildren of a Node, calling fn each time, using this as the scole..
42342      * @param {DomNode} node node to iterate children of.
42343      * @param {Function} fn method of this class to call on each item.
42344      */
42345     iterateChildren : function(node, fn)
42346     {
42347         if (!node.childNodes.length) {
42348                 return;
42349         }
42350         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42351            fn.call(this, node.childNodes[i])
42352         }
42353     },
42354     
42355     
42356     /**
42357      * cleanTableWidths.
42358      *
42359      * Quite often pasting from word etc.. results in tables with column and widths.
42360      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42361      *
42362      */
42363     cleanTableWidths : function(node)
42364     {
42365          
42366          
42367         if (!node) {
42368             this.cleanTableWidths(this.doc.body);
42369             return;
42370         }
42371         
42372         // ignore list...
42373         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42374             return; 
42375         }
42376         Roo.log(node.tagName);
42377         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42378             this.iterateChildren(node, this.cleanTableWidths);
42379             return;
42380         }
42381         if (node.hasAttribute('width')) {
42382             node.removeAttribute('width');
42383         }
42384         
42385          
42386         if (node.hasAttribute("style")) {
42387             // pretty basic...
42388             
42389             var styles = node.getAttribute("style").split(";");
42390             var nstyle = [];
42391             Roo.each(styles, function(s) {
42392                 if (!s.match(/:/)) {
42393                     return;
42394                 }
42395                 var kv = s.split(":");
42396                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42397                     return;
42398                 }
42399                 // what ever is left... we allow.
42400                 nstyle.push(s);
42401             });
42402             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42403             if (!nstyle.length) {
42404                 node.removeAttribute('style');
42405             }
42406         }
42407         
42408         this.iterateChildren(node, this.cleanTableWidths);
42409         
42410         
42411     },
42412     
42413     
42414     
42415     
42416     domToHTML : function(currentElement, depth, nopadtext) {
42417         
42418         depth = depth || 0;
42419         nopadtext = nopadtext || false;
42420     
42421         if (!currentElement) {
42422             return this.domToHTML(this.doc.body);
42423         }
42424         
42425         //Roo.log(currentElement);
42426         var j;
42427         var allText = false;
42428         var nodeName = currentElement.nodeName;
42429         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42430         
42431         if  (nodeName == '#text') {
42432             
42433             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42434         }
42435         
42436         
42437         var ret = '';
42438         if (nodeName != 'BODY') {
42439              
42440             var i = 0;
42441             // Prints the node tagName, such as <A>, <IMG>, etc
42442             if (tagName) {
42443                 var attr = [];
42444                 for(i = 0; i < currentElement.attributes.length;i++) {
42445                     // quoting?
42446                     var aname = currentElement.attributes.item(i).name;
42447                     if (!currentElement.attributes.item(i).value.length) {
42448                         continue;
42449                     }
42450                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42451                 }
42452                 
42453                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42454             } 
42455             else {
42456                 
42457                 // eack
42458             }
42459         } else {
42460             tagName = false;
42461         }
42462         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42463             return ret;
42464         }
42465         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42466             nopadtext = true;
42467         }
42468         
42469         
42470         // Traverse the tree
42471         i = 0;
42472         var currentElementChild = currentElement.childNodes.item(i);
42473         var allText = true;
42474         var innerHTML  = '';
42475         lastnode = '';
42476         while (currentElementChild) {
42477             // Formatting code (indent the tree so it looks nice on the screen)
42478             var nopad = nopadtext;
42479             if (lastnode == 'SPAN') {
42480                 nopad  = true;
42481             }
42482             // text
42483             if  (currentElementChild.nodeName == '#text') {
42484                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42485                 toadd = nopadtext ? toadd : toadd.trim();
42486                 if (!nopad && toadd.length > 80) {
42487                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42488                 }
42489                 innerHTML  += toadd;
42490                 
42491                 i++;
42492                 currentElementChild = currentElement.childNodes.item(i);
42493                 lastNode = '';
42494                 continue;
42495             }
42496             allText = false;
42497             
42498             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42499                 
42500             // Recursively traverse the tree structure of the child node
42501             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42502             lastnode = currentElementChild.nodeName;
42503             i++;
42504             currentElementChild=currentElement.childNodes.item(i);
42505         }
42506         
42507         ret += innerHTML;
42508         
42509         if (!allText) {
42510                 // The remaining code is mostly for formatting the tree
42511             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42512         }
42513         
42514         
42515         if (tagName) {
42516             ret+= "</"+tagName+">";
42517         }
42518         return ret;
42519         
42520     },
42521         
42522     applyBlacklists : function()
42523     {
42524         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42525         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42526         
42527         this.white = [];
42528         this.black = [];
42529         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42530             if (b.indexOf(tag) > -1) {
42531                 return;
42532             }
42533             this.white.push(tag);
42534             
42535         }, this);
42536         
42537         Roo.each(w, function(tag) {
42538             if (b.indexOf(tag) > -1) {
42539                 return;
42540             }
42541             if (this.white.indexOf(tag) > -1) {
42542                 return;
42543             }
42544             this.white.push(tag);
42545             
42546         }, this);
42547         
42548         
42549         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42550             if (w.indexOf(tag) > -1) {
42551                 return;
42552             }
42553             this.black.push(tag);
42554             
42555         }, this);
42556         
42557         Roo.each(b, function(tag) {
42558             if (w.indexOf(tag) > -1) {
42559                 return;
42560             }
42561             if (this.black.indexOf(tag) > -1) {
42562                 return;
42563             }
42564             this.black.push(tag);
42565             
42566         }, this);
42567         
42568         
42569         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42570         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42571         
42572         this.cwhite = [];
42573         this.cblack = [];
42574         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42575             if (b.indexOf(tag) > -1) {
42576                 return;
42577             }
42578             this.cwhite.push(tag);
42579             
42580         }, this);
42581         
42582         Roo.each(w, function(tag) {
42583             if (b.indexOf(tag) > -1) {
42584                 return;
42585             }
42586             if (this.cwhite.indexOf(tag) > -1) {
42587                 return;
42588             }
42589             this.cwhite.push(tag);
42590             
42591         }, this);
42592         
42593         
42594         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42595             if (w.indexOf(tag) > -1) {
42596                 return;
42597             }
42598             this.cblack.push(tag);
42599             
42600         }, this);
42601         
42602         Roo.each(b, function(tag) {
42603             if (w.indexOf(tag) > -1) {
42604                 return;
42605             }
42606             if (this.cblack.indexOf(tag) > -1) {
42607                 return;
42608             }
42609             this.cblack.push(tag);
42610             
42611         }, this);
42612     },
42613     
42614     setStylesheets : function(stylesheets)
42615     {
42616         if(typeof(stylesheets) == 'string'){
42617             Roo.get(this.iframe.contentDocument.head).createChild({
42618                 tag : 'link',
42619                 rel : 'stylesheet',
42620                 type : 'text/css',
42621                 href : stylesheets
42622             });
42623             
42624             return;
42625         }
42626         var _this = this;
42627      
42628         Roo.each(stylesheets, function(s) {
42629             if(!s.length){
42630                 return;
42631             }
42632             
42633             Roo.get(_this.iframe.contentDocument.head).createChild({
42634                 tag : 'link',
42635                 rel : 'stylesheet',
42636                 type : 'text/css',
42637                 href : s
42638             });
42639         });
42640
42641         
42642     },
42643     
42644     removeStylesheets : function()
42645     {
42646         var _this = this;
42647         
42648         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42649             s.remove();
42650         });
42651     }
42652     
42653     // hide stuff that is not compatible
42654     /**
42655      * @event blur
42656      * @hide
42657      */
42658     /**
42659      * @event change
42660      * @hide
42661      */
42662     /**
42663      * @event focus
42664      * @hide
42665      */
42666     /**
42667      * @event specialkey
42668      * @hide
42669      */
42670     /**
42671      * @cfg {String} fieldClass @hide
42672      */
42673     /**
42674      * @cfg {String} focusClass @hide
42675      */
42676     /**
42677      * @cfg {String} autoCreate @hide
42678      */
42679     /**
42680      * @cfg {String} inputType @hide
42681      */
42682     /**
42683      * @cfg {String} invalidClass @hide
42684      */
42685     /**
42686      * @cfg {String} invalidText @hide
42687      */
42688     /**
42689      * @cfg {String} msgFx @hide
42690      */
42691     /**
42692      * @cfg {String} validateOnBlur @hide
42693      */
42694 });
42695
42696 Roo.HtmlEditorCore.white = [
42697         'area', 'br', 'img', 'input', 'hr', 'wbr',
42698         
42699        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42700        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42701        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42702        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42703        'table',   'ul',         'xmp', 
42704        
42705        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42706       'thead',   'tr', 
42707      
42708       'dir', 'menu', 'ol', 'ul', 'dl',
42709        
42710       'embed',  'object'
42711 ];
42712
42713
42714 Roo.HtmlEditorCore.black = [
42715     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42716         'applet', // 
42717         'base',   'basefont', 'bgsound', 'blink',  'body', 
42718         'frame',  'frameset', 'head',    'html',   'ilayer', 
42719         'iframe', 'layer',  'link',     'meta',    'object',   
42720         'script', 'style' ,'title',  'xml' // clean later..
42721 ];
42722 Roo.HtmlEditorCore.clean = [
42723     'script', 'style', 'title', 'xml'
42724 ];
42725 Roo.HtmlEditorCore.remove = [
42726     'font'
42727 ];
42728 // attributes..
42729
42730 Roo.HtmlEditorCore.ablack = [
42731     'on'
42732 ];
42733     
42734 Roo.HtmlEditorCore.aclean = [ 
42735     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42736 ];
42737
42738 // protocols..
42739 Roo.HtmlEditorCore.pwhite= [
42740         'http',  'https',  'mailto'
42741 ];
42742
42743 // white listed style attributes.
42744 Roo.HtmlEditorCore.cwhite= [
42745       //  'text-align', /// default is to allow most things..
42746       
42747          
42748 //        'font-size'//??
42749 ];
42750
42751 // black listed style attributes.
42752 Roo.HtmlEditorCore.cblack= [
42753       //  'font-size' -- this can be set by the project 
42754 ];
42755
42756
42757 Roo.HtmlEditorCore.swapCodes   =[ 
42758     [    8211, "--" ], 
42759     [    8212, "--" ], 
42760     [    8216,  "'" ],  
42761     [    8217, "'" ],  
42762     [    8220, '"' ],  
42763     [    8221, '"' ],  
42764     [    8226, "*" ],  
42765     [    8230, "..." ]
42766 ]; 
42767
42768     //<script type="text/javascript">
42769
42770 /*
42771  * Ext JS Library 1.1.1
42772  * Copyright(c) 2006-2007, Ext JS, LLC.
42773  * Licence LGPL
42774  * 
42775  */
42776  
42777  
42778 Roo.form.HtmlEditor = function(config){
42779     
42780     
42781     
42782     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42783     
42784     if (!this.toolbars) {
42785         this.toolbars = [];
42786     }
42787     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42788     
42789     
42790 };
42791
42792 /**
42793  * @class Roo.form.HtmlEditor
42794  * @extends Roo.form.Field
42795  * Provides a lightweight HTML Editor component.
42796  *
42797  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42798  * 
42799  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42800  * supported by this editor.</b><br/><br/>
42801  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42802  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42803  */
42804 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42805     /**
42806      * @cfg {Boolean} clearUp
42807      */
42808     clearUp : true,
42809       /**
42810      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42811      */
42812     toolbars : false,
42813    
42814      /**
42815      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42816      *                        Roo.resizable.
42817      */
42818     resizable : false,
42819      /**
42820      * @cfg {Number} height (in pixels)
42821      */   
42822     height: 300,
42823    /**
42824      * @cfg {Number} width (in pixels)
42825      */   
42826     width: 500,
42827     
42828     /**
42829      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42830      * 
42831      */
42832     stylesheets: false,
42833     
42834     
42835      /**
42836      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42837      * 
42838      */
42839     cblack: false,
42840     /**
42841      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42842      * 
42843      */
42844     cwhite: false,
42845     
42846      /**
42847      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42848      * 
42849      */
42850     black: false,
42851     /**
42852      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42853      * 
42854      */
42855     white: false,
42856     
42857     // id of frame..
42858     frameId: false,
42859     
42860     // private properties
42861     validationEvent : false,
42862     deferHeight: true,
42863     initialized : false,
42864     activated : false,
42865     
42866     onFocus : Roo.emptyFn,
42867     iframePad:3,
42868     hideMode:'offsets',
42869     
42870     actionMode : 'container', // defaults to hiding it...
42871     
42872     defaultAutoCreate : { // modified by initCompnoent..
42873         tag: "textarea",
42874         style:"width:500px;height:300px;",
42875         autocomplete: "new-password"
42876     },
42877
42878     // private
42879     initComponent : function(){
42880         this.addEvents({
42881             /**
42882              * @event initialize
42883              * Fires when the editor is fully initialized (including the iframe)
42884              * @param {HtmlEditor} this
42885              */
42886             initialize: true,
42887             /**
42888              * @event activate
42889              * Fires when the editor is first receives the focus. Any insertion must wait
42890              * until after this event.
42891              * @param {HtmlEditor} this
42892              */
42893             activate: true,
42894              /**
42895              * @event beforesync
42896              * Fires before the textarea is updated with content from the editor iframe. Return false
42897              * to cancel the sync.
42898              * @param {HtmlEditor} this
42899              * @param {String} html
42900              */
42901             beforesync: true,
42902              /**
42903              * @event beforepush
42904              * Fires before the iframe editor is updated with content from the textarea. Return false
42905              * to cancel the push.
42906              * @param {HtmlEditor} this
42907              * @param {String} html
42908              */
42909             beforepush: true,
42910              /**
42911              * @event sync
42912              * Fires when the textarea is updated with content from the editor iframe.
42913              * @param {HtmlEditor} this
42914              * @param {String} html
42915              */
42916             sync: true,
42917              /**
42918              * @event push
42919              * Fires when the iframe editor is updated with content from the textarea.
42920              * @param {HtmlEditor} this
42921              * @param {String} html
42922              */
42923             push: true,
42924              /**
42925              * @event editmodechange
42926              * Fires when the editor switches edit modes
42927              * @param {HtmlEditor} this
42928              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42929              */
42930             editmodechange: true,
42931             /**
42932              * @event editorevent
42933              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42934              * @param {HtmlEditor} this
42935              */
42936             editorevent: true,
42937             /**
42938              * @event firstfocus
42939              * Fires when on first focus - needed by toolbars..
42940              * @param {HtmlEditor} this
42941              */
42942             firstfocus: true,
42943             /**
42944              * @event autosave
42945              * Auto save the htmlEditor value as a file into Events
42946              * @param {HtmlEditor} this
42947              */
42948             autosave: true,
42949             /**
42950              * @event savedpreview
42951              * preview the saved version of htmlEditor
42952              * @param {HtmlEditor} this
42953              */
42954             savedpreview: true,
42955             
42956             /**
42957             * @event stylesheetsclick
42958             * Fires when press the Sytlesheets button
42959             * @param {Roo.HtmlEditorCore} this
42960             */
42961             stylesheetsclick: true
42962         });
42963         this.defaultAutoCreate =  {
42964             tag: "textarea",
42965             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42966             autocomplete: "new-password"
42967         };
42968     },
42969
42970     /**
42971      * Protected method that will not generally be called directly. It
42972      * is called when the editor creates its toolbar. Override this method if you need to
42973      * add custom toolbar buttons.
42974      * @param {HtmlEditor} editor
42975      */
42976     createToolbar : function(editor){
42977         Roo.log("create toolbars");
42978         if (!editor.toolbars || !editor.toolbars.length) {
42979             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42980         }
42981         
42982         for (var i =0 ; i < editor.toolbars.length;i++) {
42983             editor.toolbars[i] = Roo.factory(
42984                     typeof(editor.toolbars[i]) == 'string' ?
42985                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42986                 Roo.form.HtmlEditor);
42987             editor.toolbars[i].init(editor);
42988         }
42989          
42990         
42991     },
42992
42993      
42994     // private
42995     onRender : function(ct, position)
42996     {
42997         var _t = this;
42998         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42999         
43000         this.wrap = this.el.wrap({
43001             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43002         });
43003         
43004         this.editorcore.onRender(ct, position);
43005          
43006         if (this.resizable) {
43007             this.resizeEl = new Roo.Resizable(this.wrap, {
43008                 pinned : true,
43009                 wrap: true,
43010                 dynamic : true,
43011                 minHeight : this.height,
43012                 height: this.height,
43013                 handles : this.resizable,
43014                 width: this.width,
43015                 listeners : {
43016                     resize : function(r, w, h) {
43017                         _t.onResize(w,h); // -something
43018                     }
43019                 }
43020             });
43021             
43022         }
43023         this.createToolbar(this);
43024        
43025         
43026         if(!this.width){
43027             this.setSize(this.wrap.getSize());
43028         }
43029         if (this.resizeEl) {
43030             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43031             // should trigger onReize..
43032         }
43033         
43034         this.keyNav = new Roo.KeyNav(this.el, {
43035             
43036             "tab" : function(e){
43037                 e.preventDefault();
43038                 
43039                 var value = this.getValue();
43040                 
43041                 var start = this.el.dom.selectionStart;
43042                 var end = this.el.dom.selectionEnd;
43043                 
43044                 if(!e.shiftKey){
43045                     
43046                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43047                     this.el.dom.setSelectionRange(end + 1, end + 1);
43048                     return;
43049                 }
43050                 
43051                 var f = value.substring(0, start).split("\t");
43052                 
43053                 if(f.pop().length != 0){
43054                     return;
43055                 }
43056                 
43057                 this.setValue(f.join("\t") + value.substring(end));
43058                 this.el.dom.setSelectionRange(start - 1, start - 1);
43059                 
43060             },
43061             
43062             "home" : function(e){
43063                 e.preventDefault();
43064                 
43065                 var curr = this.el.dom.selectionStart;
43066                 var lines = this.getValue().split("\n");
43067                 
43068                 if(!lines.length){
43069                     return;
43070                 }
43071                 
43072                 if(e.ctrlKey){
43073                     this.el.dom.setSelectionRange(0, 0);
43074                     return;
43075                 }
43076                 
43077                 var pos = 0;
43078                 
43079                 for (var i = 0; i < lines.length;i++) {
43080                     pos += lines[i].length;
43081                     
43082                     if(i != 0){
43083                         pos += 1;
43084                     }
43085                     
43086                     if(pos < curr){
43087                         continue;
43088                     }
43089                     
43090                     pos -= lines[i].length;
43091                     
43092                     break;
43093                 }
43094                 
43095                 if(!e.shiftKey){
43096                     this.el.dom.setSelectionRange(pos, pos);
43097                     return;
43098                 }
43099                 
43100                 this.el.dom.selectionStart = pos;
43101                 this.el.dom.selectionEnd = curr;
43102             },
43103             
43104             "end" : function(e){
43105                 e.preventDefault();
43106                 
43107                 var curr = this.el.dom.selectionStart;
43108                 var lines = this.getValue().split("\n");
43109                 
43110                 if(!lines.length){
43111                     return;
43112                 }
43113                 
43114                 if(e.ctrlKey){
43115                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43116                     return;
43117                 }
43118                 
43119                 var pos = 0;
43120                 
43121                 for (var i = 0; i < lines.length;i++) {
43122                     
43123                     pos += lines[i].length;
43124                     
43125                     if(i != 0){
43126                         pos += 1;
43127                     }
43128                     
43129                     if(pos < curr){
43130                         continue;
43131                     }
43132                     
43133                     break;
43134                 }
43135                 
43136                 if(!e.shiftKey){
43137                     this.el.dom.setSelectionRange(pos, pos);
43138                     return;
43139                 }
43140                 
43141                 this.el.dom.selectionStart = curr;
43142                 this.el.dom.selectionEnd = pos;
43143             },
43144
43145             scope : this,
43146
43147             doRelay : function(foo, bar, hname){
43148                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43149             },
43150
43151             forceKeyDown: true
43152         });
43153         
43154 //        if(this.autosave && this.w){
43155 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43156 //        }
43157     },
43158
43159     // private
43160     onResize : function(w, h)
43161     {
43162         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43163         var ew = false;
43164         var eh = false;
43165         
43166         if(this.el ){
43167             if(typeof w == 'number'){
43168                 var aw = w - this.wrap.getFrameWidth('lr');
43169                 this.el.setWidth(this.adjustWidth('textarea', aw));
43170                 ew = aw;
43171             }
43172             if(typeof h == 'number'){
43173                 var tbh = 0;
43174                 for (var i =0; i < this.toolbars.length;i++) {
43175                     // fixme - ask toolbars for heights?
43176                     tbh += this.toolbars[i].tb.el.getHeight();
43177                     if (this.toolbars[i].footer) {
43178                         tbh += this.toolbars[i].footer.el.getHeight();
43179                     }
43180                 }
43181                 
43182                 
43183                 
43184                 
43185                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43186                 ah -= 5; // knock a few pixes off for look..
43187 //                Roo.log(ah);
43188                 this.el.setHeight(this.adjustWidth('textarea', ah));
43189                 var eh = ah;
43190             }
43191         }
43192         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43193         this.editorcore.onResize(ew,eh);
43194         
43195     },
43196
43197     /**
43198      * Toggles the editor between standard and source edit mode.
43199      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43200      */
43201     toggleSourceEdit : function(sourceEditMode)
43202     {
43203         this.editorcore.toggleSourceEdit(sourceEditMode);
43204         
43205         if(this.editorcore.sourceEditMode){
43206             Roo.log('editor - showing textarea');
43207             
43208 //            Roo.log('in');
43209 //            Roo.log(this.syncValue());
43210             this.editorcore.syncValue();
43211             this.el.removeClass('x-hidden');
43212             this.el.dom.removeAttribute('tabIndex');
43213             this.el.focus();
43214             
43215             for (var i = 0; i < this.toolbars.length; i++) {
43216                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43217                     this.toolbars[i].tb.hide();
43218                     this.toolbars[i].footer.hide();
43219                 }
43220             }
43221             
43222         }else{
43223             Roo.log('editor - hiding textarea');
43224 //            Roo.log('out')
43225 //            Roo.log(this.pushValue()); 
43226             this.editorcore.pushValue();
43227             
43228             this.el.addClass('x-hidden');
43229             this.el.dom.setAttribute('tabIndex', -1);
43230             
43231             for (var i = 0; i < this.toolbars.length; i++) {
43232                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43233                     this.toolbars[i].tb.show();
43234                     this.toolbars[i].footer.show();
43235                 }
43236             }
43237             
43238             //this.deferFocus();
43239         }
43240         
43241         this.setSize(this.wrap.getSize());
43242         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43243         
43244         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43245     },
43246  
43247     // private (for BoxComponent)
43248     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43249
43250     // private (for BoxComponent)
43251     getResizeEl : function(){
43252         return this.wrap;
43253     },
43254
43255     // private (for BoxComponent)
43256     getPositionEl : function(){
43257         return this.wrap;
43258     },
43259
43260     // private
43261     initEvents : function(){
43262         this.originalValue = this.getValue();
43263     },
43264
43265     /**
43266      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43267      * @method
43268      */
43269     markInvalid : Roo.emptyFn,
43270     /**
43271      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43272      * @method
43273      */
43274     clearInvalid : Roo.emptyFn,
43275
43276     setValue : function(v){
43277         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43278         this.editorcore.pushValue();
43279     },
43280
43281      
43282     // private
43283     deferFocus : function(){
43284         this.focus.defer(10, this);
43285     },
43286
43287     // doc'ed in Field
43288     focus : function(){
43289         this.editorcore.focus();
43290         
43291     },
43292       
43293
43294     // private
43295     onDestroy : function(){
43296         
43297         
43298         
43299         if(this.rendered){
43300             
43301             for (var i =0; i < this.toolbars.length;i++) {
43302                 // fixme - ask toolbars for heights?
43303                 this.toolbars[i].onDestroy();
43304             }
43305             
43306             this.wrap.dom.innerHTML = '';
43307             this.wrap.remove();
43308         }
43309     },
43310
43311     // private
43312     onFirstFocus : function(){
43313         //Roo.log("onFirstFocus");
43314         this.editorcore.onFirstFocus();
43315          for (var i =0; i < this.toolbars.length;i++) {
43316             this.toolbars[i].onFirstFocus();
43317         }
43318         
43319     },
43320     
43321     // private
43322     syncValue : function()
43323     {
43324         this.editorcore.syncValue();
43325     },
43326     
43327     pushValue : function()
43328     {
43329         this.editorcore.pushValue();
43330     },
43331     
43332     setStylesheets : function(stylesheets)
43333     {
43334         this.editorcore.setStylesheets(stylesheets);
43335     },
43336     
43337     removeStylesheets : function()
43338     {
43339         this.editorcore.removeStylesheets();
43340     }
43341      
43342     
43343     // hide stuff that is not compatible
43344     /**
43345      * @event blur
43346      * @hide
43347      */
43348     /**
43349      * @event change
43350      * @hide
43351      */
43352     /**
43353      * @event focus
43354      * @hide
43355      */
43356     /**
43357      * @event specialkey
43358      * @hide
43359      */
43360     /**
43361      * @cfg {String} fieldClass @hide
43362      */
43363     /**
43364      * @cfg {String} focusClass @hide
43365      */
43366     /**
43367      * @cfg {String} autoCreate @hide
43368      */
43369     /**
43370      * @cfg {String} inputType @hide
43371      */
43372     /**
43373      * @cfg {String} invalidClass @hide
43374      */
43375     /**
43376      * @cfg {String} invalidText @hide
43377      */
43378     /**
43379      * @cfg {String} msgFx @hide
43380      */
43381     /**
43382      * @cfg {String} validateOnBlur @hide
43383      */
43384 });
43385  
43386     // <script type="text/javascript">
43387 /*
43388  * Based on
43389  * Ext JS Library 1.1.1
43390  * Copyright(c) 2006-2007, Ext JS, LLC.
43391  *  
43392  
43393  */
43394
43395 /**
43396  * @class Roo.form.HtmlEditorToolbar1
43397  * Basic Toolbar
43398  * 
43399  * Usage:
43400  *
43401  new Roo.form.HtmlEditor({
43402     ....
43403     toolbars : [
43404         new Roo.form.HtmlEditorToolbar1({
43405             disable : { fonts: 1 , format: 1, ..., ... , ...],
43406             btns : [ .... ]
43407         })
43408     }
43409      
43410  * 
43411  * @cfg {Object} disable List of elements to disable..
43412  * @cfg {Array} btns List of additional buttons.
43413  * 
43414  * 
43415  * NEEDS Extra CSS? 
43416  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43417  */
43418  
43419 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43420 {
43421     
43422     Roo.apply(this, config);
43423     
43424     // default disabled, based on 'good practice'..
43425     this.disable = this.disable || {};
43426     Roo.applyIf(this.disable, {
43427         fontSize : true,
43428         colors : true,
43429         specialElements : true
43430     });
43431     
43432     
43433     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43434     // dont call parent... till later.
43435 }
43436
43437 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43438     
43439     tb: false,
43440     
43441     rendered: false,
43442     
43443     editor : false,
43444     editorcore : false,
43445     /**
43446      * @cfg {Object} disable  List of toolbar elements to disable
43447          
43448      */
43449     disable : false,
43450     
43451     
43452      /**
43453      * @cfg {String} createLinkText The default text for the create link prompt
43454      */
43455     createLinkText : 'Please enter the URL for the link:',
43456     /**
43457      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43458      */
43459     defaultLinkValue : 'http:/'+'/',
43460    
43461     
43462       /**
43463      * @cfg {Array} fontFamilies An array of available font families
43464      */
43465     fontFamilies : [
43466         'Arial',
43467         'Courier New',
43468         'Tahoma',
43469         'Times New Roman',
43470         'Verdana'
43471     ],
43472     
43473     specialChars : [
43474            "&#169;",
43475           "&#174;",     
43476           "&#8482;",    
43477           "&#163;" ,    
43478          // "&#8212;",    
43479           "&#8230;",    
43480           "&#247;" ,    
43481         //  "&#225;" ,     ?? a acute?
43482            "&#8364;"    , //Euro
43483        //   "&#8220;"    ,
43484         //  "&#8221;"    ,
43485         //  "&#8226;"    ,
43486           "&#176;"  //   , // degrees
43487
43488          // "&#233;"     , // e ecute
43489          // "&#250;"     , // u ecute?
43490     ],
43491     
43492     specialElements : [
43493         {
43494             text: "Insert Table",
43495             xtype: 'MenuItem',
43496             xns : Roo.Menu,
43497             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43498                 
43499         },
43500         {    
43501             text: "Insert Image",
43502             xtype: 'MenuItem',
43503             xns : Roo.Menu,
43504             ihtml : '<img src="about:blank"/>'
43505             
43506         }
43507         
43508          
43509     ],
43510     
43511     
43512     inputElements : [ 
43513             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43514             "input:submit", "input:button", "select", "textarea", "label" ],
43515     formats : [
43516         ["p"] ,  
43517         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43518         ["pre"],[ "code"], 
43519         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43520         ['div'],['span']
43521     ],
43522     
43523     cleanStyles : [
43524         "font-size"
43525     ],
43526      /**
43527      * @cfg {String} defaultFont default font to use.
43528      */
43529     defaultFont: 'tahoma',
43530    
43531     fontSelect : false,
43532     
43533     
43534     formatCombo : false,
43535     
43536     init : function(editor)
43537     {
43538         this.editor = editor;
43539         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43540         var editorcore = this.editorcore;
43541         
43542         var _t = this;
43543         
43544         var fid = editorcore.frameId;
43545         var etb = this;
43546         function btn(id, toggle, handler){
43547             var xid = fid + '-'+ id ;
43548             return {
43549                 id : xid,
43550                 cmd : id,
43551                 cls : 'x-btn-icon x-edit-'+id,
43552                 enableToggle:toggle !== false,
43553                 scope: _t, // was editor...
43554                 handler:handler||_t.relayBtnCmd,
43555                 clickEvent:'mousedown',
43556                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43557                 tabIndex:-1
43558             };
43559         }
43560         
43561         
43562         
43563         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43564         this.tb = tb;
43565          // stop form submits
43566         tb.el.on('click', function(e){
43567             e.preventDefault(); // what does this do?
43568         });
43569
43570         if(!this.disable.font) { // && !Roo.isSafari){
43571             /* why no safari for fonts 
43572             editor.fontSelect = tb.el.createChild({
43573                 tag:'select',
43574                 tabIndex: -1,
43575                 cls:'x-font-select',
43576                 html: this.createFontOptions()
43577             });
43578             
43579             editor.fontSelect.on('change', function(){
43580                 var font = editor.fontSelect.dom.value;
43581                 editor.relayCmd('fontname', font);
43582                 editor.deferFocus();
43583             }, editor);
43584             
43585             tb.add(
43586                 editor.fontSelect.dom,
43587                 '-'
43588             );
43589             */
43590             
43591         };
43592         if(!this.disable.formats){
43593             this.formatCombo = new Roo.form.ComboBox({
43594                 store: new Roo.data.SimpleStore({
43595                     id : 'tag',
43596                     fields: ['tag'],
43597                     data : this.formats // from states.js
43598                 }),
43599                 blockFocus : true,
43600                 name : '',
43601                 //autoCreate : {tag: "div",  size: "20"},
43602                 displayField:'tag',
43603                 typeAhead: false,
43604                 mode: 'local',
43605                 editable : false,
43606                 triggerAction: 'all',
43607                 emptyText:'Add tag',
43608                 selectOnFocus:true,
43609                 width:135,
43610                 listeners : {
43611                     'select': function(c, r, i) {
43612                         editorcore.insertTag(r.get('tag'));
43613                         editor.focus();
43614                     }
43615                 }
43616
43617             });
43618             tb.addField(this.formatCombo);
43619             
43620         }
43621         
43622         if(!this.disable.format){
43623             tb.add(
43624                 btn('bold'),
43625                 btn('italic'),
43626                 btn('underline')
43627             );
43628         };
43629         if(!this.disable.fontSize){
43630             tb.add(
43631                 '-',
43632                 
43633                 
43634                 btn('increasefontsize', false, editorcore.adjustFont),
43635                 btn('decreasefontsize', false, editorcore.adjustFont)
43636             );
43637         };
43638         
43639         
43640         if(!this.disable.colors){
43641             tb.add(
43642                 '-', {
43643                     id:editorcore.frameId +'-forecolor',
43644                     cls:'x-btn-icon x-edit-forecolor',
43645                     clickEvent:'mousedown',
43646                     tooltip: this.buttonTips['forecolor'] || undefined,
43647                     tabIndex:-1,
43648                     menu : new Roo.menu.ColorMenu({
43649                         allowReselect: true,
43650                         focus: Roo.emptyFn,
43651                         value:'000000',
43652                         plain:true,
43653                         selectHandler: function(cp, color){
43654                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43655                             editor.deferFocus();
43656                         },
43657                         scope: editorcore,
43658                         clickEvent:'mousedown'
43659                     })
43660                 }, {
43661                     id:editorcore.frameId +'backcolor',
43662                     cls:'x-btn-icon x-edit-backcolor',
43663                     clickEvent:'mousedown',
43664                     tooltip: this.buttonTips['backcolor'] || undefined,
43665                     tabIndex:-1,
43666                     menu : new Roo.menu.ColorMenu({
43667                         focus: Roo.emptyFn,
43668                         value:'FFFFFF',
43669                         plain:true,
43670                         allowReselect: true,
43671                         selectHandler: function(cp, color){
43672                             if(Roo.isGecko){
43673                                 editorcore.execCmd('useCSS', false);
43674                                 editorcore.execCmd('hilitecolor', color);
43675                                 editorcore.execCmd('useCSS', true);
43676                                 editor.deferFocus();
43677                             }else{
43678                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43679                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43680                                 editor.deferFocus();
43681                             }
43682                         },
43683                         scope:editorcore,
43684                         clickEvent:'mousedown'
43685                     })
43686                 }
43687             );
43688         };
43689         // now add all the items...
43690         
43691
43692         if(!this.disable.alignments){
43693             tb.add(
43694                 '-',
43695                 btn('justifyleft'),
43696                 btn('justifycenter'),
43697                 btn('justifyright')
43698             );
43699         };
43700
43701         //if(!Roo.isSafari){
43702             if(!this.disable.links){
43703                 tb.add(
43704                     '-',
43705                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43706                 );
43707             };
43708
43709             if(!this.disable.lists){
43710                 tb.add(
43711                     '-',
43712                     btn('insertorderedlist'),
43713                     btn('insertunorderedlist')
43714                 );
43715             }
43716             if(!this.disable.sourceEdit){
43717                 tb.add(
43718                     '-',
43719                     btn('sourceedit', true, function(btn){
43720                         this.toggleSourceEdit(btn.pressed);
43721                     })
43722                 );
43723             }
43724         //}
43725         
43726         var smenu = { };
43727         // special menu.. - needs to be tidied up..
43728         if (!this.disable.special) {
43729             smenu = {
43730                 text: "&#169;",
43731                 cls: 'x-edit-none',
43732                 
43733                 menu : {
43734                     items : []
43735                 }
43736             };
43737             for (var i =0; i < this.specialChars.length; i++) {
43738                 smenu.menu.items.push({
43739                     
43740                     html: this.specialChars[i],
43741                     handler: function(a,b) {
43742                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43743                         //editor.insertAtCursor(a.html);
43744                         
43745                     },
43746                     tabIndex:-1
43747                 });
43748             }
43749             
43750             
43751             tb.add(smenu);
43752             
43753             
43754         }
43755         
43756         var cmenu = { };
43757         if (!this.disable.cleanStyles) {
43758             cmenu = {
43759                 cls: 'x-btn-icon x-btn-clear',
43760                 
43761                 menu : {
43762                     items : []
43763                 }
43764             };
43765             for (var i =0; i < this.cleanStyles.length; i++) {
43766                 cmenu.menu.items.push({
43767                     actiontype : this.cleanStyles[i],
43768                     html: 'Remove ' + this.cleanStyles[i],
43769                     handler: function(a,b) {
43770 //                        Roo.log(a);
43771 //                        Roo.log(b);
43772                         var c = Roo.get(editorcore.doc.body);
43773                         c.select('[style]').each(function(s) {
43774                             s.dom.style.removeProperty(a.actiontype);
43775                         });
43776                         editorcore.syncValue();
43777                     },
43778                     tabIndex:-1
43779                 });
43780             }
43781              cmenu.menu.items.push({
43782                 actiontype : 'tablewidths',
43783                 html: 'Remove Table Widths',
43784                 handler: function(a,b) {
43785                     editorcore.cleanTableWidths();
43786                     editorcore.syncValue();
43787                 },
43788                 tabIndex:-1
43789             });
43790             cmenu.menu.items.push({
43791                 actiontype : 'word',
43792                 html: 'Remove MS Word Formating',
43793                 handler: function(a,b) {
43794                     editorcore.cleanWord();
43795                     editorcore.syncValue();
43796                 },
43797                 tabIndex:-1
43798             });
43799             
43800             cmenu.menu.items.push({
43801                 actiontype : 'all',
43802                 html: 'Remove All Styles',
43803                 handler: function(a,b) {
43804                     
43805                     var c = Roo.get(editorcore.doc.body);
43806                     c.select('[style]').each(function(s) {
43807                         s.dom.removeAttribute('style');
43808                     });
43809                     editorcore.syncValue();
43810                 },
43811                 tabIndex:-1
43812             });
43813              cmenu.menu.items.push({
43814                 actiontype : 'tidy',
43815                 html: 'Tidy HTML Source',
43816                 handler: function(a,b) {
43817                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43818                     editorcore.syncValue();
43819                 },
43820                 tabIndex:-1
43821             });
43822             
43823             
43824             tb.add(cmenu);
43825         }
43826          
43827         if (!this.disable.specialElements) {
43828             var semenu = {
43829                 text: "Other;",
43830                 cls: 'x-edit-none',
43831                 menu : {
43832                     items : []
43833                 }
43834             };
43835             for (var i =0; i < this.specialElements.length; i++) {
43836                 semenu.menu.items.push(
43837                     Roo.apply({ 
43838                         handler: function(a,b) {
43839                             editor.insertAtCursor(this.ihtml);
43840                         }
43841                     }, this.specialElements[i])
43842                 );
43843                     
43844             }
43845             
43846             tb.add(semenu);
43847             
43848             
43849         }
43850          
43851         
43852         if (this.btns) {
43853             for(var i =0; i< this.btns.length;i++) {
43854                 var b = Roo.factory(this.btns[i],Roo.form);
43855                 b.cls =  'x-edit-none';
43856                 
43857                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43858                     b.cls += ' x-init-enable';
43859                 }
43860                 
43861                 b.scope = editorcore;
43862                 tb.add(b);
43863             }
43864         
43865         }
43866         
43867         
43868         
43869         // disable everything...
43870         
43871         this.tb.items.each(function(item){
43872             
43873            if(
43874                 item.id != editorcore.frameId+ '-sourceedit' && 
43875                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43876             ){
43877                 
43878                 item.disable();
43879             }
43880         });
43881         this.rendered = true;
43882         
43883         // the all the btns;
43884         editor.on('editorevent', this.updateToolbar, this);
43885         // other toolbars need to implement this..
43886         //editor.on('editmodechange', this.updateToolbar, this);
43887     },
43888     
43889     
43890     relayBtnCmd : function(btn) {
43891         this.editorcore.relayCmd(btn.cmd);
43892     },
43893     // private used internally
43894     createLink : function(){
43895         Roo.log("create link?");
43896         var url = prompt(this.createLinkText, this.defaultLinkValue);
43897         if(url && url != 'http:/'+'/'){
43898             this.editorcore.relayCmd('createlink', url);
43899         }
43900     },
43901
43902     
43903     /**
43904      * Protected method that will not generally be called directly. It triggers
43905      * a toolbar update by reading the markup state of the current selection in the editor.
43906      */
43907     updateToolbar: function(){
43908
43909         if(!this.editorcore.activated){
43910             this.editor.onFirstFocus();
43911             return;
43912         }
43913
43914         var btns = this.tb.items.map, 
43915             doc = this.editorcore.doc,
43916             frameId = this.editorcore.frameId;
43917
43918         if(!this.disable.font && !Roo.isSafari){
43919             /*
43920             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43921             if(name != this.fontSelect.dom.value){
43922                 this.fontSelect.dom.value = name;
43923             }
43924             */
43925         }
43926         if(!this.disable.format){
43927             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43928             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43929             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43930         }
43931         if(!this.disable.alignments){
43932             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43933             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43934             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43935         }
43936         if(!Roo.isSafari && !this.disable.lists){
43937             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43938             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43939         }
43940         
43941         var ans = this.editorcore.getAllAncestors();
43942         if (this.formatCombo) {
43943             
43944             
43945             var store = this.formatCombo.store;
43946             this.formatCombo.setValue("");
43947             for (var i =0; i < ans.length;i++) {
43948                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43949                     // select it..
43950                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43951                     break;
43952                 }
43953             }
43954         }
43955         
43956         
43957         
43958         // hides menus... - so this cant be on a menu...
43959         Roo.menu.MenuMgr.hideAll();
43960
43961         //this.editorsyncValue();
43962     },
43963    
43964     
43965     createFontOptions : function(){
43966         var buf = [], fs = this.fontFamilies, ff, lc;
43967         
43968         
43969         
43970         for(var i = 0, len = fs.length; i< len; i++){
43971             ff = fs[i];
43972             lc = ff.toLowerCase();
43973             buf.push(
43974                 '<option value="',lc,'" style="font-family:',ff,';"',
43975                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43976                     ff,
43977                 '</option>'
43978             );
43979         }
43980         return buf.join('');
43981     },
43982     
43983     toggleSourceEdit : function(sourceEditMode){
43984         
43985         Roo.log("toolbar toogle");
43986         if(sourceEditMode === undefined){
43987             sourceEditMode = !this.sourceEditMode;
43988         }
43989         this.sourceEditMode = sourceEditMode === true;
43990         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43991         // just toggle the button?
43992         if(btn.pressed !== this.sourceEditMode){
43993             btn.toggle(this.sourceEditMode);
43994             return;
43995         }
43996         
43997         if(sourceEditMode){
43998             Roo.log("disabling buttons");
43999             this.tb.items.each(function(item){
44000                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44001                     item.disable();
44002                 }
44003             });
44004           
44005         }else{
44006             Roo.log("enabling buttons");
44007             if(this.editorcore.initialized){
44008                 this.tb.items.each(function(item){
44009                     item.enable();
44010                 });
44011             }
44012             
44013         }
44014         Roo.log("calling toggole on editor");
44015         // tell the editor that it's been pressed..
44016         this.editor.toggleSourceEdit(sourceEditMode);
44017        
44018     },
44019      /**
44020      * Object collection of toolbar tooltips for the buttons in the editor. The key
44021      * is the command id associated with that button and the value is a valid QuickTips object.
44022      * For example:
44023 <pre><code>
44024 {
44025     bold : {
44026         title: 'Bold (Ctrl+B)',
44027         text: 'Make the selected text bold.',
44028         cls: 'x-html-editor-tip'
44029     },
44030     italic : {
44031         title: 'Italic (Ctrl+I)',
44032         text: 'Make the selected text italic.',
44033         cls: 'x-html-editor-tip'
44034     },
44035     ...
44036 </code></pre>
44037     * @type Object
44038      */
44039     buttonTips : {
44040         bold : {
44041             title: 'Bold (Ctrl+B)',
44042             text: 'Make the selected text bold.',
44043             cls: 'x-html-editor-tip'
44044         },
44045         italic : {
44046             title: 'Italic (Ctrl+I)',
44047             text: 'Make the selected text italic.',
44048             cls: 'x-html-editor-tip'
44049         },
44050         underline : {
44051             title: 'Underline (Ctrl+U)',
44052             text: 'Underline the selected text.',
44053             cls: 'x-html-editor-tip'
44054         },
44055         increasefontsize : {
44056             title: 'Grow Text',
44057             text: 'Increase the font size.',
44058             cls: 'x-html-editor-tip'
44059         },
44060         decreasefontsize : {
44061             title: 'Shrink Text',
44062             text: 'Decrease the font size.',
44063             cls: 'x-html-editor-tip'
44064         },
44065         backcolor : {
44066             title: 'Text Highlight Color',
44067             text: 'Change the background color of the selected text.',
44068             cls: 'x-html-editor-tip'
44069         },
44070         forecolor : {
44071             title: 'Font Color',
44072             text: 'Change the color of the selected text.',
44073             cls: 'x-html-editor-tip'
44074         },
44075         justifyleft : {
44076             title: 'Align Text Left',
44077             text: 'Align text to the left.',
44078             cls: 'x-html-editor-tip'
44079         },
44080         justifycenter : {
44081             title: 'Center Text',
44082             text: 'Center text in the editor.',
44083             cls: 'x-html-editor-tip'
44084         },
44085         justifyright : {
44086             title: 'Align Text Right',
44087             text: 'Align text to the right.',
44088             cls: 'x-html-editor-tip'
44089         },
44090         insertunorderedlist : {
44091             title: 'Bullet List',
44092             text: 'Start a bulleted list.',
44093             cls: 'x-html-editor-tip'
44094         },
44095         insertorderedlist : {
44096             title: 'Numbered List',
44097             text: 'Start a numbered list.',
44098             cls: 'x-html-editor-tip'
44099         },
44100         createlink : {
44101             title: 'Hyperlink',
44102             text: 'Make the selected text a hyperlink.',
44103             cls: 'x-html-editor-tip'
44104         },
44105         sourceedit : {
44106             title: 'Source Edit',
44107             text: 'Switch to source editing mode.',
44108             cls: 'x-html-editor-tip'
44109         }
44110     },
44111     // private
44112     onDestroy : function(){
44113         if(this.rendered){
44114             
44115             this.tb.items.each(function(item){
44116                 if(item.menu){
44117                     item.menu.removeAll();
44118                     if(item.menu.el){
44119                         item.menu.el.destroy();
44120                     }
44121                 }
44122                 item.destroy();
44123             });
44124              
44125         }
44126     },
44127     onFirstFocus: function() {
44128         this.tb.items.each(function(item){
44129            item.enable();
44130         });
44131     }
44132 });
44133
44134
44135
44136
44137 // <script type="text/javascript">
44138 /*
44139  * Based on
44140  * Ext JS Library 1.1.1
44141  * Copyright(c) 2006-2007, Ext JS, LLC.
44142  *  
44143  
44144  */
44145
44146  
44147 /**
44148  * @class Roo.form.HtmlEditor.ToolbarContext
44149  * Context Toolbar
44150  * 
44151  * Usage:
44152  *
44153  new Roo.form.HtmlEditor({
44154     ....
44155     toolbars : [
44156         { xtype: 'ToolbarStandard', styles : {} }
44157         { xtype: 'ToolbarContext', disable : {} }
44158     ]
44159 })
44160
44161      
44162  * 
44163  * @config : {Object} disable List of elements to disable.. (not done yet.)
44164  * @config : {Object} styles  Map of styles available.
44165  * 
44166  */
44167
44168 Roo.form.HtmlEditor.ToolbarContext = function(config)
44169 {
44170     
44171     Roo.apply(this, config);
44172     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44173     // dont call parent... till later.
44174     this.styles = this.styles || {};
44175 }
44176
44177  
44178
44179 Roo.form.HtmlEditor.ToolbarContext.types = {
44180     'IMG' : {
44181         width : {
44182             title: "Width",
44183             width: 40
44184         },
44185         height:  {
44186             title: "Height",
44187             width: 40
44188         },
44189         align: {
44190             title: "Align",
44191             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44192             width : 80
44193             
44194         },
44195         border: {
44196             title: "Border",
44197             width: 40
44198         },
44199         alt: {
44200             title: "Alt",
44201             width: 120
44202         },
44203         src : {
44204             title: "Src",
44205             width: 220
44206         }
44207         
44208     },
44209     'A' : {
44210         name : {
44211             title: "Name",
44212             width: 50
44213         },
44214         target:  {
44215             title: "Target",
44216             width: 120
44217         },
44218         href:  {
44219             title: "Href",
44220             width: 220
44221         } // border?
44222         
44223     },
44224     'TABLE' : {
44225         rows : {
44226             title: "Rows",
44227             width: 20
44228         },
44229         cols : {
44230             title: "Cols",
44231             width: 20
44232         },
44233         width : {
44234             title: "Width",
44235             width: 40
44236         },
44237         height : {
44238             title: "Height",
44239             width: 40
44240         },
44241         border : {
44242             title: "Border",
44243             width: 20
44244         }
44245     },
44246     'TD' : {
44247         width : {
44248             title: "Width",
44249             width: 40
44250         },
44251         height : {
44252             title: "Height",
44253             width: 40
44254         },   
44255         align: {
44256             title: "Align",
44257             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44258             width: 80
44259         },
44260         valign: {
44261             title: "Valign",
44262             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44263             width: 80
44264         },
44265         colspan: {
44266             title: "Colspan",
44267             width: 20
44268             
44269         },
44270          'font-family'  : {
44271             title : "Font",
44272             style : 'fontFamily',
44273             displayField: 'display',
44274             optname : 'font-family',
44275             width: 140
44276         }
44277     },
44278     'INPUT' : {
44279         name : {
44280             title: "name",
44281             width: 120
44282         },
44283         value : {
44284             title: "Value",
44285             width: 120
44286         },
44287         width : {
44288             title: "Width",
44289             width: 40
44290         }
44291     },
44292     'LABEL' : {
44293         'for' : {
44294             title: "For",
44295             width: 120
44296         }
44297     },
44298     'TEXTAREA' : {
44299           name : {
44300             title: "name",
44301             width: 120
44302         },
44303         rows : {
44304             title: "Rows",
44305             width: 20
44306         },
44307         cols : {
44308             title: "Cols",
44309             width: 20
44310         }
44311     },
44312     'SELECT' : {
44313         name : {
44314             title: "name",
44315             width: 120
44316         },
44317         selectoptions : {
44318             title: "Options",
44319             width: 200
44320         }
44321     },
44322     
44323     // should we really allow this??
44324     // should this just be 
44325     'BODY' : {
44326         title : {
44327             title: "Title",
44328             width: 200,
44329             disabled : true
44330         }
44331     },
44332     'SPAN' : {
44333         'font-family'  : {
44334             title : "Font",
44335             style : 'fontFamily',
44336             displayField: 'display',
44337             optname : 'font-family',
44338             width: 140
44339         }
44340     },
44341     'DIV' : {
44342         'font-family'  : {
44343             title : "Font",
44344             style : 'fontFamily',
44345             displayField: 'display',
44346             optname : 'font-family',
44347             width: 140
44348         }
44349     },
44350      'P' : {
44351         'font-family'  : {
44352             title : "Font",
44353             style : 'fontFamily',
44354             displayField: 'display',
44355             optname : 'font-family',
44356             width: 140
44357         }
44358     },
44359     
44360     '*' : {
44361         // empty..
44362     }
44363
44364 };
44365
44366 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44367 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44368
44369 Roo.form.HtmlEditor.ToolbarContext.options = {
44370         'font-family'  : [ 
44371                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44372                 [ 'Courier New', 'Courier New'],
44373                 [ 'Tahoma', 'Tahoma'],
44374                 [ 'Times New Roman,serif', 'Times'],
44375                 [ 'Verdana','Verdana' ]
44376         ]
44377 };
44378
44379 // fixme - these need to be configurable..
44380  
44381
44382 Roo.form.HtmlEditor.ToolbarContext.types
44383
44384
44385 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44386     
44387     tb: false,
44388     
44389     rendered: false,
44390     
44391     editor : false,
44392     editorcore : false,
44393     /**
44394      * @cfg {Object} disable  List of toolbar elements to disable
44395          
44396      */
44397     disable : false,
44398     /**
44399      * @cfg {Object} styles List of styles 
44400      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44401      *
44402      * These must be defined in the page, so they get rendered correctly..
44403      * .headline { }
44404      * TD.underline { }
44405      * 
44406      */
44407     styles : false,
44408     
44409     options: false,
44410     
44411     toolbars : false,
44412     
44413     init : function(editor)
44414     {
44415         this.editor = editor;
44416         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44417         var editorcore = this.editorcore;
44418         
44419         var fid = editorcore.frameId;
44420         var etb = this;
44421         function btn(id, toggle, handler){
44422             var xid = fid + '-'+ id ;
44423             return {
44424                 id : xid,
44425                 cmd : id,
44426                 cls : 'x-btn-icon x-edit-'+id,
44427                 enableToggle:toggle !== false,
44428                 scope: editorcore, // was editor...
44429                 handler:handler||editorcore.relayBtnCmd,
44430                 clickEvent:'mousedown',
44431                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44432                 tabIndex:-1
44433             };
44434         }
44435         // create a new element.
44436         var wdiv = editor.wrap.createChild({
44437                 tag: 'div'
44438             }, editor.wrap.dom.firstChild.nextSibling, true);
44439         
44440         // can we do this more than once??
44441         
44442          // stop form submits
44443       
44444  
44445         // disable everything...
44446         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44447         this.toolbars = {};
44448            
44449         for (var i in  ty) {
44450           
44451             this.toolbars[i] = this.buildToolbar(ty[i],i);
44452         }
44453         this.tb = this.toolbars.BODY;
44454         this.tb.el.show();
44455         this.buildFooter();
44456         this.footer.show();
44457         editor.on('hide', function( ) { this.footer.hide() }, this);
44458         editor.on('show', function( ) { this.footer.show() }, this);
44459         
44460          
44461         this.rendered = true;
44462         
44463         // the all the btns;
44464         editor.on('editorevent', this.updateToolbar, this);
44465         // other toolbars need to implement this..
44466         //editor.on('editmodechange', this.updateToolbar, this);
44467     },
44468     
44469     
44470     
44471     /**
44472      * Protected method that will not generally be called directly. It triggers
44473      * a toolbar update by reading the markup state of the current selection in the editor.
44474      *
44475      * Note you can force an update by calling on('editorevent', scope, false)
44476      */
44477     updateToolbar: function(editor,ev,sel){
44478
44479         //Roo.log(ev);
44480         // capture mouse up - this is handy for selecting images..
44481         // perhaps should go somewhere else...
44482         if(!this.editorcore.activated){
44483              this.editor.onFirstFocus();
44484             return;
44485         }
44486         
44487         
44488         
44489         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44490         // selectNode - might want to handle IE?
44491         if (ev &&
44492             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44493             ev.target && ev.target.tagName == 'IMG') {
44494             // they have click on an image...
44495             // let's see if we can change the selection...
44496             sel = ev.target;
44497          
44498               var nodeRange = sel.ownerDocument.createRange();
44499             try {
44500                 nodeRange.selectNode(sel);
44501             } catch (e) {
44502                 nodeRange.selectNodeContents(sel);
44503             }
44504             //nodeRange.collapse(true);
44505             var s = this.editorcore.win.getSelection();
44506             s.removeAllRanges();
44507             s.addRange(nodeRange);
44508         }  
44509         
44510       
44511         var updateFooter = sel ? false : true;
44512         
44513         
44514         var ans = this.editorcore.getAllAncestors();
44515         
44516         // pick
44517         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44518         
44519         if (!sel) { 
44520             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44521             sel = sel ? sel : this.editorcore.doc.body;
44522             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44523             
44524         }
44525         // pick a menu that exists..
44526         var tn = sel.tagName.toUpperCase();
44527         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44528         
44529         tn = sel.tagName.toUpperCase();
44530         
44531         var lastSel = this.tb.selectedNode
44532         
44533         this.tb.selectedNode = sel;
44534         
44535         // if current menu does not match..
44536         
44537         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44538                 
44539             this.tb.el.hide();
44540             ///console.log("show: " + tn);
44541             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44542             this.tb.el.show();
44543             // update name
44544             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44545             
44546             
44547             // update attributes
44548             if (this.tb.fields) {
44549                 this.tb.fields.each(function(e) {
44550                     if (e.stylename) {
44551                         e.setValue(sel.style[e.stylename]);
44552                         return;
44553                     } 
44554                    e.setValue(sel.getAttribute(e.attrname));
44555                 });
44556             }
44557             
44558             var hasStyles = false;
44559             for(var i in this.styles) {
44560                 hasStyles = true;
44561                 break;
44562             }
44563             
44564             // update styles
44565             if (hasStyles) { 
44566                 var st = this.tb.fields.item(0);
44567                 
44568                 st.store.removeAll();
44569                
44570                 
44571                 var cn = sel.className.split(/\s+/);
44572                 
44573                 var avs = [];
44574                 if (this.styles['*']) {
44575                     
44576                     Roo.each(this.styles['*'], function(v) {
44577                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44578                     });
44579                 }
44580                 if (this.styles[tn]) { 
44581                     Roo.each(this.styles[tn], function(v) {
44582                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44583                     });
44584                 }
44585                 
44586                 st.store.loadData(avs);
44587                 st.collapse();
44588                 st.setValue(cn);
44589             }
44590             // flag our selected Node.
44591             this.tb.selectedNode = sel;
44592            
44593            
44594             Roo.menu.MenuMgr.hideAll();
44595
44596         }
44597         
44598         if (!updateFooter) {
44599             //this.footDisp.dom.innerHTML = ''; 
44600             return;
44601         }
44602         // update the footer
44603         //
44604         var html = '';
44605         
44606         this.footerEls = ans.reverse();
44607         Roo.each(this.footerEls, function(a,i) {
44608             if (!a) { return; }
44609             html += html.length ? ' &gt; '  :  '';
44610             
44611             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44612             
44613         });
44614        
44615         // 
44616         var sz = this.footDisp.up('td').getSize();
44617         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44618         this.footDisp.dom.style.marginLeft = '5px';
44619         
44620         this.footDisp.dom.style.overflow = 'hidden';
44621         
44622         this.footDisp.dom.innerHTML = html;
44623             
44624         //this.editorsyncValue();
44625     },
44626      
44627     
44628    
44629        
44630     // private
44631     onDestroy : function(){
44632         if(this.rendered){
44633             
44634             this.tb.items.each(function(item){
44635                 if(item.menu){
44636                     item.menu.removeAll();
44637                     if(item.menu.el){
44638                         item.menu.el.destroy();
44639                     }
44640                 }
44641                 item.destroy();
44642             });
44643              
44644         }
44645     },
44646     onFirstFocus: function() {
44647         // need to do this for all the toolbars..
44648         this.tb.items.each(function(item){
44649            item.enable();
44650         });
44651     },
44652     buildToolbar: function(tlist, nm)
44653     {
44654         var editor = this.editor;
44655         var editorcore = this.editorcore;
44656          // create a new element.
44657         var wdiv = editor.wrap.createChild({
44658                 tag: 'div'
44659             }, editor.wrap.dom.firstChild.nextSibling, true);
44660         
44661        
44662         var tb = new Roo.Toolbar(wdiv);
44663         // add the name..
44664         
44665         tb.add(nm+ ":&nbsp;");
44666         
44667         var styles = [];
44668         for(var i in this.styles) {
44669             styles.push(i);
44670         }
44671         
44672         // styles...
44673         if (styles && styles.length) {
44674             
44675             // this needs a multi-select checkbox...
44676             tb.addField( new Roo.form.ComboBox({
44677                 store: new Roo.data.SimpleStore({
44678                     id : 'val',
44679                     fields: ['val', 'selected'],
44680                     data : [] 
44681                 }),
44682                 name : '-roo-edit-className',
44683                 attrname : 'className',
44684                 displayField: 'val',
44685                 typeAhead: false,
44686                 mode: 'local',
44687                 editable : false,
44688                 triggerAction: 'all',
44689                 emptyText:'Select Style',
44690                 selectOnFocus:true,
44691                 width: 130,
44692                 listeners : {
44693                     'select': function(c, r, i) {
44694                         // initial support only for on class per el..
44695                         tb.selectedNode.className =  r ? r.get('val') : '';
44696                         editorcore.syncValue();
44697                     }
44698                 }
44699     
44700             }));
44701         }
44702         
44703         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44704         var tbops = tbc.options;
44705         
44706         for (var i in tlist) {
44707             
44708             var item = tlist[i];
44709             tb.add(item.title + ":&nbsp;");
44710             
44711             
44712             //optname == used so you can configure the options available..
44713             var opts = item.opts ? item.opts : false;
44714             if (item.optname) {
44715                 opts = tbops[item.optname];
44716            
44717             }
44718             
44719             if (opts) {
44720                 // opts == pulldown..
44721                 tb.addField( new Roo.form.ComboBox({
44722                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44723                         id : 'val',
44724                         fields: ['val', 'display'],
44725                         data : opts  
44726                     }),
44727                     name : '-roo-edit-' + i,
44728                     attrname : i,
44729                     stylename : item.style ? item.style : false,
44730                     displayField: item.displayField ? item.displayField : 'val',
44731                     valueField :  'val',
44732                     typeAhead: false,
44733                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44734                     editable : false,
44735                     triggerAction: 'all',
44736                     emptyText:'Select',
44737                     selectOnFocus:true,
44738                     width: item.width ? item.width  : 130,
44739                     listeners : {
44740                         'select': function(c, r, i) {
44741                             if (c.stylename) {
44742                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44743                                 return;
44744                             }
44745                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44746                         }
44747                     }
44748
44749                 }));
44750                 continue;
44751                     
44752                  
44753                 
44754                 tb.addField( new Roo.form.TextField({
44755                     name: i,
44756                     width: 100,
44757                     //allowBlank:false,
44758                     value: ''
44759                 }));
44760                 continue;
44761             }
44762             tb.addField( new Roo.form.TextField({
44763                 name: '-roo-edit-' + i,
44764                 attrname : i,
44765                 
44766                 width: item.width,
44767                 //allowBlank:true,
44768                 value: '',
44769                 listeners: {
44770                     'change' : function(f, nv, ov) {
44771                         tb.selectedNode.setAttribute(f.attrname, nv);
44772                     }
44773                 }
44774             }));
44775              
44776         }
44777         
44778         var _this = this;
44779         
44780         if(nm == 'BODY'){
44781             tb.addSeparator();
44782         
44783             tb.addButton( {
44784                 text: 'Stylesheets',
44785
44786                 listeners : {
44787                     click : function ()
44788                     {
44789                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44790                     }
44791                 }
44792             });
44793         }
44794         
44795         tb.addFill();
44796         tb.addButton( {
44797             text: 'Remove Tag',
44798     
44799             listeners : {
44800                 click : function ()
44801                 {
44802                     // remove
44803                     // undo does not work.
44804                      
44805                     var sn = tb.selectedNode;
44806                     
44807                     var pn = sn.parentNode;
44808                     
44809                     var stn =  sn.childNodes[0];
44810                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44811                     while (sn.childNodes.length) {
44812                         var node = sn.childNodes[0];
44813                         sn.removeChild(node);
44814                         //Roo.log(node);
44815                         pn.insertBefore(node, sn);
44816                         
44817                     }
44818                     pn.removeChild(sn);
44819                     var range = editorcore.createRange();
44820         
44821                     range.setStart(stn,0);
44822                     range.setEnd(en,0); //????
44823                     //range.selectNode(sel);
44824                     
44825                     
44826                     var selection = editorcore.getSelection();
44827                     selection.removeAllRanges();
44828                     selection.addRange(range);
44829                     
44830                     
44831                     
44832                     //_this.updateToolbar(null, null, pn);
44833                     _this.updateToolbar(null, null, null);
44834                     _this.footDisp.dom.innerHTML = ''; 
44835                 }
44836             }
44837             
44838                     
44839                 
44840             
44841         });
44842         
44843         
44844         tb.el.on('click', function(e){
44845             e.preventDefault(); // what does this do?
44846         });
44847         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44848         tb.el.hide();
44849         tb.name = nm;
44850         // dont need to disable them... as they will get hidden
44851         return tb;
44852          
44853         
44854     },
44855     buildFooter : function()
44856     {
44857         
44858         var fel = this.editor.wrap.createChild();
44859         this.footer = new Roo.Toolbar(fel);
44860         // toolbar has scrolly on left / right?
44861         var footDisp= new Roo.Toolbar.Fill();
44862         var _t = this;
44863         this.footer.add(
44864             {
44865                 text : '&lt;',
44866                 xtype: 'Button',
44867                 handler : function() {
44868                     _t.footDisp.scrollTo('left',0,true)
44869                 }
44870             }
44871         );
44872         this.footer.add( footDisp );
44873         this.footer.add( 
44874             {
44875                 text : '&gt;',
44876                 xtype: 'Button',
44877                 handler : function() {
44878                     // no animation..
44879                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44880                 }
44881             }
44882         );
44883         var fel = Roo.get(footDisp.el);
44884         fel.addClass('x-editor-context');
44885         this.footDispWrap = fel; 
44886         this.footDispWrap.overflow  = 'hidden';
44887         
44888         this.footDisp = fel.createChild();
44889         this.footDispWrap.on('click', this.onContextClick, this)
44890         
44891         
44892     },
44893     onContextClick : function (ev,dom)
44894     {
44895         ev.preventDefault();
44896         var  cn = dom.className;
44897         //Roo.log(cn);
44898         if (!cn.match(/x-ed-loc-/)) {
44899             return;
44900         }
44901         var n = cn.split('-').pop();
44902         var ans = this.footerEls;
44903         var sel = ans[n];
44904         
44905          // pick
44906         var range = this.editorcore.createRange();
44907         
44908         range.selectNodeContents(sel);
44909         //range.selectNode(sel);
44910         
44911         
44912         var selection = this.editorcore.getSelection();
44913         selection.removeAllRanges();
44914         selection.addRange(range);
44915         
44916         
44917         
44918         this.updateToolbar(null, null, sel);
44919         
44920         
44921     }
44922     
44923     
44924     
44925     
44926     
44927 });
44928
44929
44930
44931
44932
44933 /*
44934  * Based on:
44935  * Ext JS Library 1.1.1
44936  * Copyright(c) 2006-2007, Ext JS, LLC.
44937  *
44938  * Originally Released Under LGPL - original licence link has changed is not relivant.
44939  *
44940  * Fork - LGPL
44941  * <script type="text/javascript">
44942  */
44943  
44944 /**
44945  * @class Roo.form.BasicForm
44946  * @extends Roo.util.Observable
44947  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44948  * @constructor
44949  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44950  * @param {Object} config Configuration options
44951  */
44952 Roo.form.BasicForm = function(el, config){
44953     this.allItems = [];
44954     this.childForms = [];
44955     Roo.apply(this, config);
44956     /*
44957      * The Roo.form.Field items in this form.
44958      * @type MixedCollection
44959      */
44960      
44961      
44962     this.items = new Roo.util.MixedCollection(false, function(o){
44963         return o.id || (o.id = Roo.id());
44964     });
44965     this.addEvents({
44966         /**
44967          * @event beforeaction
44968          * Fires before any action is performed. Return false to cancel the action.
44969          * @param {Form} this
44970          * @param {Action} action The action to be performed
44971          */
44972         beforeaction: true,
44973         /**
44974          * @event actionfailed
44975          * Fires when an action fails.
44976          * @param {Form} this
44977          * @param {Action} action The action that failed
44978          */
44979         actionfailed : true,
44980         /**
44981          * @event actioncomplete
44982          * Fires when an action is completed.
44983          * @param {Form} this
44984          * @param {Action} action The action that completed
44985          */
44986         actioncomplete : true
44987     });
44988     if(el){
44989         this.initEl(el);
44990     }
44991     Roo.form.BasicForm.superclass.constructor.call(this);
44992 };
44993
44994 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44995     /**
44996      * @cfg {String} method
44997      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44998      */
44999     /**
45000      * @cfg {DataReader} reader
45001      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45002      * This is optional as there is built-in support for processing JSON.
45003      */
45004     /**
45005      * @cfg {DataReader} errorReader
45006      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45007      * This is completely optional as there is built-in support for processing JSON.
45008      */
45009     /**
45010      * @cfg {String} url
45011      * The URL to use for form actions if one isn't supplied in the action options.
45012      */
45013     /**
45014      * @cfg {Boolean} fileUpload
45015      * Set to true if this form is a file upload.
45016      */
45017      
45018     /**
45019      * @cfg {Object} baseParams
45020      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45021      */
45022      /**
45023      
45024     /**
45025      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45026      */
45027     timeout: 30,
45028
45029     // private
45030     activeAction : null,
45031
45032     /**
45033      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45034      * or setValues() data instead of when the form was first created.
45035      */
45036     trackResetOnLoad : false,
45037     
45038     
45039     /**
45040      * childForms - used for multi-tab forms
45041      * @type {Array}
45042      */
45043     childForms : false,
45044     
45045     /**
45046      * allItems - full list of fields.
45047      * @type {Array}
45048      */
45049     allItems : false,
45050     
45051     /**
45052      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45053      * element by passing it or its id or mask the form itself by passing in true.
45054      * @type Mixed
45055      */
45056     waitMsgTarget : false,
45057
45058     // private
45059     initEl : function(el){
45060         this.el = Roo.get(el);
45061         this.id = this.el.id || Roo.id();
45062         this.el.on('submit', this.onSubmit, this);
45063         this.el.addClass('x-form');
45064     },
45065
45066     // private
45067     onSubmit : function(e){
45068         e.stopEvent();
45069     },
45070
45071     /**
45072      * Returns true if client-side validation on the form is successful.
45073      * @return Boolean
45074      */
45075     isValid : function(){
45076         var valid = true;
45077         this.items.each(function(f){
45078            if(!f.validate()){
45079                valid = false;
45080            }
45081         });
45082         return valid;
45083     },
45084
45085     /**
45086      * Returns true if any fields in this form have changed since their original load.
45087      * @return Boolean
45088      */
45089     isDirty : function(){
45090         var dirty = false;
45091         this.items.each(function(f){
45092            if(f.isDirty()){
45093                dirty = true;
45094                return false;
45095            }
45096         });
45097         return dirty;
45098     },
45099
45100     /**
45101      * Performs a predefined action (submit or load) or custom actions you define on this form.
45102      * @param {String} actionName The name of the action type
45103      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45104      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45105      * accept other config options):
45106      * <pre>
45107 Property          Type             Description
45108 ----------------  ---------------  ----------------------------------------------------------------------------------
45109 url               String           The url for the action (defaults to the form's url)
45110 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45111 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45112 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45113                                    validate the form on the client (defaults to false)
45114      * </pre>
45115      * @return {BasicForm} this
45116      */
45117     doAction : function(action, options){
45118         if(typeof action == 'string'){
45119             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45120         }
45121         if(this.fireEvent('beforeaction', this, action) !== false){
45122             this.beforeAction(action);
45123             action.run.defer(100, action);
45124         }
45125         return this;
45126     },
45127
45128     /**
45129      * Shortcut to do a submit action.
45130      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45131      * @return {BasicForm} this
45132      */
45133     submit : function(options){
45134         this.doAction('submit', options);
45135         return this;
45136     },
45137
45138     /**
45139      * Shortcut to do a load action.
45140      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45141      * @return {BasicForm} this
45142      */
45143     load : function(options){
45144         this.doAction('load', options);
45145         return this;
45146     },
45147
45148     /**
45149      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45150      * @param {Record} record The record to edit
45151      * @return {BasicForm} this
45152      */
45153     updateRecord : function(record){
45154         record.beginEdit();
45155         var fs = record.fields;
45156         fs.each(function(f){
45157             var field = this.findField(f.name);
45158             if(field){
45159                 record.set(f.name, field.getValue());
45160             }
45161         }, this);
45162         record.endEdit();
45163         return this;
45164     },
45165
45166     /**
45167      * Loads an Roo.data.Record into this form.
45168      * @param {Record} record The record to load
45169      * @return {BasicForm} this
45170      */
45171     loadRecord : function(record){
45172         this.setValues(record.data);
45173         return this;
45174     },
45175
45176     // private
45177     beforeAction : function(action){
45178         var o = action.options;
45179         
45180        
45181         if(this.waitMsgTarget === true){
45182             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45183         }else if(this.waitMsgTarget){
45184             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45185             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45186         }else {
45187             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45188         }
45189          
45190     },
45191
45192     // private
45193     afterAction : function(action, success){
45194         this.activeAction = null;
45195         var o = action.options;
45196         
45197         if(this.waitMsgTarget === true){
45198             this.el.unmask();
45199         }else if(this.waitMsgTarget){
45200             this.waitMsgTarget.unmask();
45201         }else{
45202             Roo.MessageBox.updateProgress(1);
45203             Roo.MessageBox.hide();
45204         }
45205          
45206         if(success){
45207             if(o.reset){
45208                 this.reset();
45209             }
45210             Roo.callback(o.success, o.scope, [this, action]);
45211             this.fireEvent('actioncomplete', this, action);
45212             
45213         }else{
45214             
45215             // failure condition..
45216             // we have a scenario where updates need confirming.
45217             // eg. if a locking scenario exists..
45218             // we look for { errors : { needs_confirm : true }} in the response.
45219             if (
45220                 (typeof(action.result) != 'undefined')  &&
45221                 (typeof(action.result.errors) != 'undefined')  &&
45222                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45223            ){
45224                 var _t = this;
45225                 Roo.MessageBox.confirm(
45226                     "Change requires confirmation",
45227                     action.result.errorMsg,
45228                     function(r) {
45229                         if (r != 'yes') {
45230                             return;
45231                         }
45232                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45233                     }
45234                     
45235                 );
45236                 
45237                 
45238                 
45239                 return;
45240             }
45241             
45242             Roo.callback(o.failure, o.scope, [this, action]);
45243             // show an error message if no failed handler is set..
45244             if (!this.hasListener('actionfailed')) {
45245                 Roo.MessageBox.alert("Error",
45246                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45247                         action.result.errorMsg :
45248                         "Saving Failed, please check your entries or try again"
45249                 );
45250             }
45251             
45252             this.fireEvent('actionfailed', this, action);
45253         }
45254         
45255     },
45256
45257     /**
45258      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45259      * @param {String} id The value to search for
45260      * @return Field
45261      */
45262     findField : function(id){
45263         var field = this.items.get(id);
45264         if(!field){
45265             this.items.each(function(f){
45266                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45267                     field = f;
45268                     return false;
45269                 }
45270             });
45271         }
45272         return field || null;
45273     },
45274
45275     /**
45276      * Add a secondary form to this one, 
45277      * Used to provide tabbed forms. One form is primary, with hidden values 
45278      * which mirror the elements from the other forms.
45279      * 
45280      * @param {Roo.form.Form} form to add.
45281      * 
45282      */
45283     addForm : function(form)
45284     {
45285        
45286         if (this.childForms.indexOf(form) > -1) {
45287             // already added..
45288             return;
45289         }
45290         this.childForms.push(form);
45291         var n = '';
45292         Roo.each(form.allItems, function (fe) {
45293             
45294             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45295             if (this.findField(n)) { // already added..
45296                 return;
45297             }
45298             var add = new Roo.form.Hidden({
45299                 name : n
45300             });
45301             add.render(this.el);
45302             
45303             this.add( add );
45304         }, this);
45305         
45306     },
45307     /**
45308      * Mark fields in this form invalid in bulk.
45309      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45310      * @return {BasicForm} this
45311      */
45312     markInvalid : function(errors){
45313         if(errors instanceof Array){
45314             for(var i = 0, len = errors.length; i < len; i++){
45315                 var fieldError = errors[i];
45316                 var f = this.findField(fieldError.id);
45317                 if(f){
45318                     f.markInvalid(fieldError.msg);
45319                 }
45320             }
45321         }else{
45322             var field, id;
45323             for(id in errors){
45324                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45325                     field.markInvalid(errors[id]);
45326                 }
45327             }
45328         }
45329         Roo.each(this.childForms || [], function (f) {
45330             f.markInvalid(errors);
45331         });
45332         
45333         return this;
45334     },
45335
45336     /**
45337      * Set values for fields in this form in bulk.
45338      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45339      * @return {BasicForm} this
45340      */
45341     setValues : function(values){
45342         if(values instanceof Array){ // array of objects
45343             for(var i = 0, len = values.length; i < len; i++){
45344                 var v = values[i];
45345                 var f = this.findField(v.id);
45346                 if(f){
45347                     f.setValue(v.value);
45348                     if(this.trackResetOnLoad){
45349                         f.originalValue = f.getValue();
45350                     }
45351                 }
45352             }
45353         }else{ // object hash
45354             var field, id;
45355             for(id in values){
45356                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45357                     
45358                     if (field.setFromData && 
45359                         field.valueField && 
45360                         field.displayField &&
45361                         // combos' with local stores can 
45362                         // be queried via setValue()
45363                         // to set their value..
45364                         (field.store && !field.store.isLocal)
45365                         ) {
45366                         // it's a combo
45367                         var sd = { };
45368                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45369                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45370                         field.setFromData(sd);
45371                         
45372                     } else {
45373                         field.setValue(values[id]);
45374                     }
45375                     
45376                     
45377                     if(this.trackResetOnLoad){
45378                         field.originalValue = field.getValue();
45379                     }
45380                 }
45381             }
45382         }
45383          
45384         Roo.each(this.childForms || [], function (f) {
45385             f.setValues(values);
45386         });
45387                 
45388         return this;
45389     },
45390
45391     /**
45392      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45393      * they are returned as an array.
45394      * @param {Boolean} asString
45395      * @return {Object}
45396      */
45397     getValues : function(asString){
45398         if (this.childForms) {
45399             // copy values from the child forms
45400             Roo.each(this.childForms, function (f) {
45401                 this.setValues(f.getValues());
45402             }, this);
45403         }
45404         
45405         
45406         
45407         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45408         if(asString === true){
45409             return fs;
45410         }
45411         return Roo.urlDecode(fs);
45412     },
45413     
45414     /**
45415      * Returns the fields in this form as an object with key/value pairs. 
45416      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45417      * @return {Object}
45418      */
45419     getFieldValues : function(with_hidden)
45420     {
45421         if (this.childForms) {
45422             // copy values from the child forms
45423             // should this call getFieldValues - probably not as we do not currently copy
45424             // hidden fields when we generate..
45425             Roo.each(this.childForms, function (f) {
45426                 this.setValues(f.getValues());
45427             }, this);
45428         }
45429         
45430         var ret = {};
45431         this.items.each(function(f){
45432             if (!f.getName()) {
45433                 return;
45434             }
45435             var v = f.getValue();
45436             if (f.inputType =='radio') {
45437                 if (typeof(ret[f.getName()]) == 'undefined') {
45438                     ret[f.getName()] = ''; // empty..
45439                 }
45440                 
45441                 if (!f.el.dom.checked) {
45442                     return;
45443                     
45444                 }
45445                 v = f.el.dom.value;
45446                 
45447             }
45448             
45449             // not sure if this supported any more..
45450             if ((typeof(v) == 'object') && f.getRawValue) {
45451                 v = f.getRawValue() ; // dates..
45452             }
45453             // combo boxes where name != hiddenName...
45454             if (f.name != f.getName()) {
45455                 ret[f.name] = f.getRawValue();
45456             }
45457             ret[f.getName()] = v;
45458         });
45459         
45460         return ret;
45461     },
45462
45463     /**
45464      * Clears all invalid messages in this form.
45465      * @return {BasicForm} this
45466      */
45467     clearInvalid : function(){
45468         this.items.each(function(f){
45469            f.clearInvalid();
45470         });
45471         
45472         Roo.each(this.childForms || [], function (f) {
45473             f.clearInvalid();
45474         });
45475         
45476         
45477         return this;
45478     },
45479
45480     /**
45481      * Resets this form.
45482      * @return {BasicForm} this
45483      */
45484     reset : function(){
45485         this.items.each(function(f){
45486             f.reset();
45487         });
45488         
45489         Roo.each(this.childForms || [], function (f) {
45490             f.reset();
45491         });
45492        
45493         
45494         return this;
45495     },
45496
45497     /**
45498      * Add Roo.form components to this form.
45499      * @param {Field} field1
45500      * @param {Field} field2 (optional)
45501      * @param {Field} etc (optional)
45502      * @return {BasicForm} this
45503      */
45504     add : function(){
45505         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45506         return this;
45507     },
45508
45509
45510     /**
45511      * Removes a field from the items collection (does NOT remove its markup).
45512      * @param {Field} field
45513      * @return {BasicForm} this
45514      */
45515     remove : function(field){
45516         this.items.remove(field);
45517         return this;
45518     },
45519
45520     /**
45521      * Looks at the fields in this form, checks them for an id attribute,
45522      * and calls applyTo on the existing dom element with that id.
45523      * @return {BasicForm} this
45524      */
45525     render : function(){
45526         this.items.each(function(f){
45527             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45528                 f.applyTo(f.id);
45529             }
45530         });
45531         return this;
45532     },
45533
45534     /**
45535      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45536      * @param {Object} values
45537      * @return {BasicForm} this
45538      */
45539     applyToFields : function(o){
45540         this.items.each(function(f){
45541            Roo.apply(f, o);
45542         });
45543         return this;
45544     },
45545
45546     /**
45547      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45548      * @param {Object} values
45549      * @return {BasicForm} this
45550      */
45551     applyIfToFields : function(o){
45552         this.items.each(function(f){
45553            Roo.applyIf(f, o);
45554         });
45555         return this;
45556     }
45557 });
45558
45559 // back compat
45560 Roo.BasicForm = Roo.form.BasicForm;/*
45561  * Based on:
45562  * Ext JS Library 1.1.1
45563  * Copyright(c) 2006-2007, Ext JS, LLC.
45564  *
45565  * Originally Released Under LGPL - original licence link has changed is not relivant.
45566  *
45567  * Fork - LGPL
45568  * <script type="text/javascript">
45569  */
45570
45571 /**
45572  * @class Roo.form.Form
45573  * @extends Roo.form.BasicForm
45574  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45575  * @constructor
45576  * @param {Object} config Configuration options
45577  */
45578 Roo.form.Form = function(config){
45579     var xitems =  [];
45580     if (config.items) {
45581         xitems = config.items;
45582         delete config.items;
45583     }
45584    
45585     
45586     Roo.form.Form.superclass.constructor.call(this, null, config);
45587     this.url = this.url || this.action;
45588     if(!this.root){
45589         this.root = new Roo.form.Layout(Roo.applyIf({
45590             id: Roo.id()
45591         }, config));
45592     }
45593     this.active = this.root;
45594     /**
45595      * Array of all the buttons that have been added to this form via {@link addButton}
45596      * @type Array
45597      */
45598     this.buttons = [];
45599     this.allItems = [];
45600     this.addEvents({
45601         /**
45602          * @event clientvalidation
45603          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45604          * @param {Form} this
45605          * @param {Boolean} valid true if the form has passed client-side validation
45606          */
45607         clientvalidation: true,
45608         /**
45609          * @event rendered
45610          * Fires when the form is rendered
45611          * @param {Roo.form.Form} form
45612          */
45613         rendered : true
45614     });
45615     
45616     if (this.progressUrl) {
45617             // push a hidden field onto the list of fields..
45618             this.addxtype( {
45619                     xns: Roo.form, 
45620                     xtype : 'Hidden', 
45621                     name : 'UPLOAD_IDENTIFIER' 
45622             });
45623         }
45624         
45625     
45626     Roo.each(xitems, this.addxtype, this);
45627     
45628     
45629     
45630 };
45631
45632 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45633     /**
45634      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45635      */
45636     /**
45637      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45638      */
45639     /**
45640      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45641      */
45642     buttonAlign:'center',
45643
45644     /**
45645      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45646      */
45647     minButtonWidth:75,
45648
45649     /**
45650      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45651      * This property cascades to child containers if not set.
45652      */
45653     labelAlign:'left',
45654
45655     /**
45656      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45657      * fires a looping event with that state. This is required to bind buttons to the valid
45658      * state using the config value formBind:true on the button.
45659      */
45660     monitorValid : false,
45661
45662     /**
45663      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45664      */
45665     monitorPoll : 200,
45666     
45667     /**
45668      * @cfg {String} progressUrl - Url to return progress data 
45669      */
45670     
45671     progressUrl : false,
45672   
45673     /**
45674      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45675      * fields are added and the column is closed. If no fields are passed the column remains open
45676      * until end() is called.
45677      * @param {Object} config The config to pass to the column
45678      * @param {Field} field1 (optional)
45679      * @param {Field} field2 (optional)
45680      * @param {Field} etc (optional)
45681      * @return Column The column container object
45682      */
45683     column : function(c){
45684         var col = new Roo.form.Column(c);
45685         this.start(col);
45686         if(arguments.length > 1){ // duplicate code required because of Opera
45687             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45688             this.end();
45689         }
45690         return col;
45691     },
45692
45693     /**
45694      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45695      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45696      * until end() is called.
45697      * @param {Object} config The config to pass to the fieldset
45698      * @param {Field} field1 (optional)
45699      * @param {Field} field2 (optional)
45700      * @param {Field} etc (optional)
45701      * @return FieldSet The fieldset container object
45702      */
45703     fieldset : function(c){
45704         var fs = new Roo.form.FieldSet(c);
45705         this.start(fs);
45706         if(arguments.length > 1){ // duplicate code required because of Opera
45707             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45708             this.end();
45709         }
45710         return fs;
45711     },
45712
45713     /**
45714      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45715      * fields are added and the container is closed. If no fields are passed the container remains open
45716      * until end() is called.
45717      * @param {Object} config The config to pass to the Layout
45718      * @param {Field} field1 (optional)
45719      * @param {Field} field2 (optional)
45720      * @param {Field} etc (optional)
45721      * @return Layout The container object
45722      */
45723     container : function(c){
45724         var l = new Roo.form.Layout(c);
45725         this.start(l);
45726         if(arguments.length > 1){ // duplicate code required because of Opera
45727             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45728             this.end();
45729         }
45730         return l;
45731     },
45732
45733     /**
45734      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45735      * @param {Object} container A Roo.form.Layout or subclass of Layout
45736      * @return {Form} this
45737      */
45738     start : function(c){
45739         // cascade label info
45740         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45741         this.active.stack.push(c);
45742         c.ownerCt = this.active;
45743         this.active = c;
45744         return this;
45745     },
45746
45747     /**
45748      * Closes the current open container
45749      * @return {Form} this
45750      */
45751     end : function(){
45752         if(this.active == this.root){
45753             return this;
45754         }
45755         this.active = this.active.ownerCt;
45756         return this;
45757     },
45758
45759     /**
45760      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45761      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45762      * as the label of the field.
45763      * @param {Field} field1
45764      * @param {Field} field2 (optional)
45765      * @param {Field} etc. (optional)
45766      * @return {Form} this
45767      */
45768     add : function(){
45769         this.active.stack.push.apply(this.active.stack, arguments);
45770         this.allItems.push.apply(this.allItems,arguments);
45771         var r = [];
45772         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45773             if(a[i].isFormField){
45774                 r.push(a[i]);
45775             }
45776         }
45777         if(r.length > 0){
45778             Roo.form.Form.superclass.add.apply(this, r);
45779         }
45780         return this;
45781     },
45782     
45783
45784     
45785     
45786     
45787      /**
45788      * Find any element that has been added to a form, using it's ID or name
45789      * This can include framesets, columns etc. along with regular fields..
45790      * @param {String} id - id or name to find.
45791      
45792      * @return {Element} e - or false if nothing found.
45793      */
45794     findbyId : function(id)
45795     {
45796         var ret = false;
45797         if (!id) {
45798             return ret;
45799         }
45800         Roo.each(this.allItems, function(f){
45801             if (f.id == id || f.name == id ){
45802                 ret = f;
45803                 return false;
45804             }
45805         });
45806         return ret;
45807     },
45808
45809     
45810     
45811     /**
45812      * Render this form into the passed container. This should only be called once!
45813      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45814      * @return {Form} this
45815      */
45816     render : function(ct)
45817     {
45818         
45819         
45820         
45821         ct = Roo.get(ct);
45822         var o = this.autoCreate || {
45823             tag: 'form',
45824             method : this.method || 'POST',
45825             id : this.id || Roo.id()
45826         };
45827         this.initEl(ct.createChild(o));
45828
45829         this.root.render(this.el);
45830         
45831        
45832              
45833         this.items.each(function(f){
45834             f.render('x-form-el-'+f.id);
45835         });
45836
45837         if(this.buttons.length > 0){
45838             // tables are required to maintain order and for correct IE layout
45839             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45840                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45841                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45842             }}, null, true);
45843             var tr = tb.getElementsByTagName('tr')[0];
45844             for(var i = 0, len = this.buttons.length; i < len; i++) {
45845                 var b = this.buttons[i];
45846                 var td = document.createElement('td');
45847                 td.className = 'x-form-btn-td';
45848                 b.render(tr.appendChild(td));
45849             }
45850         }
45851         if(this.monitorValid){ // initialize after render
45852             this.startMonitoring();
45853         }
45854         this.fireEvent('rendered', this);
45855         return this;
45856     },
45857
45858     /**
45859      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45860      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45861      * object or a valid Roo.DomHelper element config
45862      * @param {Function} handler The function called when the button is clicked
45863      * @param {Object} scope (optional) The scope of the handler function
45864      * @return {Roo.Button}
45865      */
45866     addButton : function(config, handler, scope){
45867         var bc = {
45868             handler: handler,
45869             scope: scope,
45870             minWidth: this.minButtonWidth,
45871             hideParent:true
45872         };
45873         if(typeof config == "string"){
45874             bc.text = config;
45875         }else{
45876             Roo.apply(bc, config);
45877         }
45878         var btn = new Roo.Button(null, bc);
45879         this.buttons.push(btn);
45880         return btn;
45881     },
45882
45883      /**
45884      * Adds a series of form elements (using the xtype property as the factory method.
45885      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45886      * @param {Object} config 
45887      */
45888     
45889     addxtype : function()
45890     {
45891         var ar = Array.prototype.slice.call(arguments, 0);
45892         var ret = false;
45893         for(var i = 0; i < ar.length; i++) {
45894             if (!ar[i]) {
45895                 continue; // skip -- if this happends something invalid got sent, we 
45896                 // should ignore it, as basically that interface element will not show up
45897                 // and that should be pretty obvious!!
45898             }
45899             
45900             if (Roo.form[ar[i].xtype]) {
45901                 ar[i].form = this;
45902                 var fe = Roo.factory(ar[i], Roo.form);
45903                 if (!ret) {
45904                     ret = fe;
45905                 }
45906                 fe.form = this;
45907                 if (fe.store) {
45908                     fe.store.form = this;
45909                 }
45910                 if (fe.isLayout) {  
45911                          
45912                     this.start(fe);
45913                     this.allItems.push(fe);
45914                     if (fe.items && fe.addxtype) {
45915                         fe.addxtype.apply(fe, fe.items);
45916                         delete fe.items;
45917                     }
45918                      this.end();
45919                     continue;
45920                 }
45921                 
45922                 
45923                  
45924                 this.add(fe);
45925               //  console.log('adding ' + ar[i].xtype);
45926             }
45927             if (ar[i].xtype == 'Button') {  
45928                 //console.log('adding button');
45929                 //console.log(ar[i]);
45930                 this.addButton(ar[i]);
45931                 this.allItems.push(fe);
45932                 continue;
45933             }
45934             
45935             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45936                 alert('end is not supported on xtype any more, use items');
45937             //    this.end();
45938             //    //console.log('adding end');
45939             }
45940             
45941         }
45942         return ret;
45943     },
45944     
45945     /**
45946      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45947      * option "monitorValid"
45948      */
45949     startMonitoring : function(){
45950         if(!this.bound){
45951             this.bound = true;
45952             Roo.TaskMgr.start({
45953                 run : this.bindHandler,
45954                 interval : this.monitorPoll || 200,
45955                 scope: this
45956             });
45957         }
45958     },
45959
45960     /**
45961      * Stops monitoring of the valid state of this form
45962      */
45963     stopMonitoring : function(){
45964         this.bound = false;
45965     },
45966
45967     // private
45968     bindHandler : function(){
45969         if(!this.bound){
45970             return false; // stops binding
45971         }
45972         var valid = true;
45973         this.items.each(function(f){
45974             if(!f.isValid(true)){
45975                 valid = false;
45976                 return false;
45977             }
45978         });
45979         for(var i = 0, len = this.buttons.length; i < len; i++){
45980             var btn = this.buttons[i];
45981             if(btn.formBind === true && btn.disabled === valid){
45982                 btn.setDisabled(!valid);
45983             }
45984         }
45985         this.fireEvent('clientvalidation', this, valid);
45986     }
45987     
45988     
45989     
45990     
45991     
45992     
45993     
45994     
45995 });
45996
45997
45998 // back compat
45999 Roo.Form = Roo.form.Form;
46000 /*
46001  * Based on:
46002  * Ext JS Library 1.1.1
46003  * Copyright(c) 2006-2007, Ext JS, LLC.
46004  *
46005  * Originally Released Under LGPL - original licence link has changed is not relivant.
46006  *
46007  * Fork - LGPL
46008  * <script type="text/javascript">
46009  */
46010
46011 // as we use this in bootstrap.
46012 Roo.namespace('Roo.form');
46013  /**
46014  * @class Roo.form.Action
46015  * Internal Class used to handle form actions
46016  * @constructor
46017  * @param {Roo.form.BasicForm} el The form element or its id
46018  * @param {Object} config Configuration options
46019  */
46020
46021  
46022  
46023 // define the action interface
46024 Roo.form.Action = function(form, options){
46025     this.form = form;
46026     this.options = options || {};
46027 };
46028 /**
46029  * Client Validation Failed
46030  * @const 
46031  */
46032 Roo.form.Action.CLIENT_INVALID = 'client';
46033 /**
46034  * Server Validation Failed
46035  * @const 
46036  */
46037 Roo.form.Action.SERVER_INVALID = 'server';
46038  /**
46039  * Connect to Server Failed
46040  * @const 
46041  */
46042 Roo.form.Action.CONNECT_FAILURE = 'connect';
46043 /**
46044  * Reading Data from Server Failed
46045  * @const 
46046  */
46047 Roo.form.Action.LOAD_FAILURE = 'load';
46048
46049 Roo.form.Action.prototype = {
46050     type : 'default',
46051     failureType : undefined,
46052     response : undefined,
46053     result : undefined,
46054
46055     // interface method
46056     run : function(options){
46057
46058     },
46059
46060     // interface method
46061     success : function(response){
46062
46063     },
46064
46065     // interface method
46066     handleResponse : function(response){
46067
46068     },
46069
46070     // default connection failure
46071     failure : function(response){
46072         
46073         this.response = response;
46074         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46075         this.form.afterAction(this, false);
46076     },
46077
46078     processResponse : function(response){
46079         this.response = response;
46080         if(!response.responseText){
46081             return true;
46082         }
46083         this.result = this.handleResponse(response);
46084         return this.result;
46085     },
46086
46087     // utility functions used internally
46088     getUrl : function(appendParams){
46089         var url = this.options.url || this.form.url || this.form.el.dom.action;
46090         if(appendParams){
46091             var p = this.getParams();
46092             if(p){
46093                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46094             }
46095         }
46096         return url;
46097     },
46098
46099     getMethod : function(){
46100         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46101     },
46102
46103     getParams : function(){
46104         var bp = this.form.baseParams;
46105         var p = this.options.params;
46106         if(p){
46107             if(typeof p == "object"){
46108                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46109             }else if(typeof p == 'string' && bp){
46110                 p += '&' + Roo.urlEncode(bp);
46111             }
46112         }else if(bp){
46113             p = Roo.urlEncode(bp);
46114         }
46115         return p;
46116     },
46117
46118     createCallback : function(){
46119         return {
46120             success: this.success,
46121             failure: this.failure,
46122             scope: this,
46123             timeout: (this.form.timeout*1000),
46124             upload: this.form.fileUpload ? this.success : undefined
46125         };
46126     }
46127 };
46128
46129 Roo.form.Action.Submit = function(form, options){
46130     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46131 };
46132
46133 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46134     type : 'submit',
46135
46136     haveProgress : false,
46137     uploadComplete : false,
46138     
46139     // uploadProgress indicator.
46140     uploadProgress : function()
46141     {
46142         if (!this.form.progressUrl) {
46143             return;
46144         }
46145         
46146         if (!this.haveProgress) {
46147             Roo.MessageBox.progress("Uploading", "Uploading");
46148         }
46149         if (this.uploadComplete) {
46150            Roo.MessageBox.hide();
46151            return;
46152         }
46153         
46154         this.haveProgress = true;
46155    
46156         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46157         
46158         var c = new Roo.data.Connection();
46159         c.request({
46160             url : this.form.progressUrl,
46161             params: {
46162                 id : uid
46163             },
46164             method: 'GET',
46165             success : function(req){
46166                //console.log(data);
46167                 var rdata = false;
46168                 var edata;
46169                 try  {
46170                    rdata = Roo.decode(req.responseText)
46171                 } catch (e) {
46172                     Roo.log("Invalid data from server..");
46173                     Roo.log(edata);
46174                     return;
46175                 }
46176                 if (!rdata || !rdata.success) {
46177                     Roo.log(rdata);
46178                     Roo.MessageBox.alert(Roo.encode(rdata));
46179                     return;
46180                 }
46181                 var data = rdata.data;
46182                 
46183                 if (this.uploadComplete) {
46184                    Roo.MessageBox.hide();
46185                    return;
46186                 }
46187                    
46188                 if (data){
46189                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46190                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46191                     );
46192                 }
46193                 this.uploadProgress.defer(2000,this);
46194             },
46195        
46196             failure: function(data) {
46197                 Roo.log('progress url failed ');
46198                 Roo.log(data);
46199             },
46200             scope : this
46201         });
46202            
46203     },
46204     
46205     
46206     run : function()
46207     {
46208         // run get Values on the form, so it syncs any secondary forms.
46209         this.form.getValues();
46210         
46211         var o = this.options;
46212         var method = this.getMethod();
46213         var isPost = method == 'POST';
46214         if(o.clientValidation === false || this.form.isValid()){
46215             
46216             if (this.form.progressUrl) {
46217                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46218                     (new Date() * 1) + '' + Math.random());
46219                     
46220             } 
46221             
46222             
46223             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46224                 form:this.form.el.dom,
46225                 url:this.getUrl(!isPost),
46226                 method: method,
46227                 params:isPost ? this.getParams() : null,
46228                 isUpload: this.form.fileUpload
46229             }));
46230             
46231             this.uploadProgress();
46232
46233         }else if (o.clientValidation !== false){ // client validation failed
46234             this.failureType = Roo.form.Action.CLIENT_INVALID;
46235             this.form.afterAction(this, false);
46236         }
46237     },
46238
46239     success : function(response)
46240     {
46241         this.uploadComplete= true;
46242         if (this.haveProgress) {
46243             Roo.MessageBox.hide();
46244         }
46245         
46246         
46247         var result = this.processResponse(response);
46248         if(result === true || result.success){
46249             this.form.afterAction(this, true);
46250             return;
46251         }
46252         if(result.errors){
46253             this.form.markInvalid(result.errors);
46254             this.failureType = Roo.form.Action.SERVER_INVALID;
46255         }
46256         this.form.afterAction(this, false);
46257     },
46258     failure : function(response)
46259     {
46260         this.uploadComplete= true;
46261         if (this.haveProgress) {
46262             Roo.MessageBox.hide();
46263         }
46264         
46265         this.response = response;
46266         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46267         this.form.afterAction(this, false);
46268     },
46269     
46270     handleResponse : function(response){
46271         if(this.form.errorReader){
46272             var rs = this.form.errorReader.read(response);
46273             var errors = [];
46274             if(rs.records){
46275                 for(var i = 0, len = rs.records.length; i < len; i++) {
46276                     var r = rs.records[i];
46277                     errors[i] = r.data;
46278                 }
46279             }
46280             if(errors.length < 1){
46281                 errors = null;
46282             }
46283             return {
46284                 success : rs.success,
46285                 errors : errors
46286             };
46287         }
46288         var ret = false;
46289         try {
46290             ret = Roo.decode(response.responseText);
46291         } catch (e) {
46292             ret = {
46293                 success: false,
46294                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46295                 errors : []
46296             };
46297         }
46298         return ret;
46299         
46300     }
46301 });
46302
46303
46304 Roo.form.Action.Load = function(form, options){
46305     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46306     this.reader = this.form.reader;
46307 };
46308
46309 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46310     type : 'load',
46311
46312     run : function(){
46313         
46314         Roo.Ajax.request(Roo.apply(
46315                 this.createCallback(), {
46316                     method:this.getMethod(),
46317                     url:this.getUrl(false),
46318                     params:this.getParams()
46319         }));
46320     },
46321
46322     success : function(response){
46323         
46324         var result = this.processResponse(response);
46325         if(result === true || !result.success || !result.data){
46326             this.failureType = Roo.form.Action.LOAD_FAILURE;
46327             this.form.afterAction(this, false);
46328             return;
46329         }
46330         this.form.clearInvalid();
46331         this.form.setValues(result.data);
46332         this.form.afterAction(this, true);
46333     },
46334
46335     handleResponse : function(response){
46336         if(this.form.reader){
46337             var rs = this.form.reader.read(response);
46338             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46339             return {
46340                 success : rs.success,
46341                 data : data
46342             };
46343         }
46344         return Roo.decode(response.responseText);
46345     }
46346 });
46347
46348 Roo.form.Action.ACTION_TYPES = {
46349     'load' : Roo.form.Action.Load,
46350     'submit' : Roo.form.Action.Submit
46351 };/*
46352  * Based on:
46353  * Ext JS Library 1.1.1
46354  * Copyright(c) 2006-2007, Ext JS, LLC.
46355  *
46356  * Originally Released Under LGPL - original licence link has changed is not relivant.
46357  *
46358  * Fork - LGPL
46359  * <script type="text/javascript">
46360  */
46361  
46362 /**
46363  * @class Roo.form.Layout
46364  * @extends Roo.Component
46365  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46366  * @constructor
46367  * @param {Object} config Configuration options
46368  */
46369 Roo.form.Layout = function(config){
46370     var xitems = [];
46371     if (config.items) {
46372         xitems = config.items;
46373         delete config.items;
46374     }
46375     Roo.form.Layout.superclass.constructor.call(this, config);
46376     this.stack = [];
46377     Roo.each(xitems, this.addxtype, this);
46378      
46379 };
46380
46381 Roo.extend(Roo.form.Layout, Roo.Component, {
46382     /**
46383      * @cfg {String/Object} autoCreate
46384      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46385      */
46386     /**
46387      * @cfg {String/Object/Function} style
46388      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46389      * a function which returns such a specification.
46390      */
46391     /**
46392      * @cfg {String} labelAlign
46393      * Valid values are "left," "top" and "right" (defaults to "left")
46394      */
46395     /**
46396      * @cfg {Number} labelWidth
46397      * Fixed width in pixels of all field labels (defaults to undefined)
46398      */
46399     /**
46400      * @cfg {Boolean} clear
46401      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46402      */
46403     clear : true,
46404     /**
46405      * @cfg {String} labelSeparator
46406      * The separator to use after field labels (defaults to ':')
46407      */
46408     labelSeparator : ':',
46409     /**
46410      * @cfg {Boolean} hideLabels
46411      * True to suppress the display of field labels in this layout (defaults to false)
46412      */
46413     hideLabels : false,
46414
46415     // private
46416     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46417     
46418     isLayout : true,
46419     
46420     // private
46421     onRender : function(ct, position){
46422         if(this.el){ // from markup
46423             this.el = Roo.get(this.el);
46424         }else {  // generate
46425             var cfg = this.getAutoCreate();
46426             this.el = ct.createChild(cfg, position);
46427         }
46428         if(this.style){
46429             this.el.applyStyles(this.style);
46430         }
46431         if(this.labelAlign){
46432             this.el.addClass('x-form-label-'+this.labelAlign);
46433         }
46434         if(this.hideLabels){
46435             this.labelStyle = "display:none";
46436             this.elementStyle = "padding-left:0;";
46437         }else{
46438             if(typeof this.labelWidth == 'number'){
46439                 this.labelStyle = "width:"+this.labelWidth+"px;";
46440                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46441             }
46442             if(this.labelAlign == 'top'){
46443                 this.labelStyle = "width:auto;";
46444                 this.elementStyle = "padding-left:0;";
46445             }
46446         }
46447         var stack = this.stack;
46448         var slen = stack.length;
46449         if(slen > 0){
46450             if(!this.fieldTpl){
46451                 var t = new Roo.Template(
46452                     '<div class="x-form-item {5}">',
46453                         '<label for="{0}" style="{2}">{1}{4}</label>',
46454                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46455                         '</div>',
46456                     '</div><div class="x-form-clear-left"></div>'
46457                 );
46458                 t.disableFormats = true;
46459                 t.compile();
46460                 Roo.form.Layout.prototype.fieldTpl = t;
46461             }
46462             for(var i = 0; i < slen; i++) {
46463                 if(stack[i].isFormField){
46464                     this.renderField(stack[i]);
46465                 }else{
46466                     this.renderComponent(stack[i]);
46467                 }
46468             }
46469         }
46470         if(this.clear){
46471             this.el.createChild({cls:'x-form-clear'});
46472         }
46473     },
46474
46475     // private
46476     renderField : function(f){
46477         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46478                f.id, //0
46479                f.fieldLabel, //1
46480                f.labelStyle||this.labelStyle||'', //2
46481                this.elementStyle||'', //3
46482                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46483                f.itemCls||this.itemCls||''  //5
46484        ], true).getPrevSibling());
46485     },
46486
46487     // private
46488     renderComponent : function(c){
46489         c.render(c.isLayout ? this.el : this.el.createChild());    
46490     },
46491     /**
46492      * Adds a object form elements (using the xtype property as the factory method.)
46493      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46494      * @param {Object} config 
46495      */
46496     addxtype : function(o)
46497     {
46498         // create the lement.
46499         o.form = this.form;
46500         var fe = Roo.factory(o, Roo.form);
46501         this.form.allItems.push(fe);
46502         this.stack.push(fe);
46503         
46504         if (fe.isFormField) {
46505             this.form.items.add(fe);
46506         }
46507          
46508         return fe;
46509     }
46510 });
46511
46512 /**
46513  * @class Roo.form.Column
46514  * @extends Roo.form.Layout
46515  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46516  * @constructor
46517  * @param {Object} config Configuration options
46518  */
46519 Roo.form.Column = function(config){
46520     Roo.form.Column.superclass.constructor.call(this, config);
46521 };
46522
46523 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46524     /**
46525      * @cfg {Number/String} width
46526      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46527      */
46528     /**
46529      * @cfg {String/Object} autoCreate
46530      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46531      */
46532
46533     // private
46534     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46535
46536     // private
46537     onRender : function(ct, position){
46538         Roo.form.Column.superclass.onRender.call(this, ct, position);
46539         if(this.width){
46540             this.el.setWidth(this.width);
46541         }
46542     }
46543 });
46544
46545
46546 /**
46547  * @class Roo.form.Row
46548  * @extends Roo.form.Layout
46549  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46550  * @constructor
46551  * @param {Object} config Configuration options
46552  */
46553
46554  
46555 Roo.form.Row = function(config){
46556     Roo.form.Row.superclass.constructor.call(this, config);
46557 };
46558  
46559 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46560       /**
46561      * @cfg {Number/String} width
46562      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46563      */
46564     /**
46565      * @cfg {Number/String} height
46566      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46567      */
46568     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46569     
46570     padWidth : 20,
46571     // private
46572     onRender : function(ct, position){
46573         //console.log('row render');
46574         if(!this.rowTpl){
46575             var t = new Roo.Template(
46576                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46577                     '<label for="{0}" style="{2}">{1}{4}</label>',
46578                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46579                     '</div>',
46580                 '</div>'
46581             );
46582             t.disableFormats = true;
46583             t.compile();
46584             Roo.form.Layout.prototype.rowTpl = t;
46585         }
46586         this.fieldTpl = this.rowTpl;
46587         
46588         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46589         var labelWidth = 100;
46590         
46591         if ((this.labelAlign != 'top')) {
46592             if (typeof this.labelWidth == 'number') {
46593                 labelWidth = this.labelWidth
46594             }
46595             this.padWidth =  20 + labelWidth;
46596             
46597         }
46598         
46599         Roo.form.Column.superclass.onRender.call(this, ct, position);
46600         if(this.width){
46601             this.el.setWidth(this.width);
46602         }
46603         if(this.height){
46604             this.el.setHeight(this.height);
46605         }
46606     },
46607     
46608     // private
46609     renderField : function(f){
46610         f.fieldEl = this.fieldTpl.append(this.el, [
46611                f.id, f.fieldLabel,
46612                f.labelStyle||this.labelStyle||'',
46613                this.elementStyle||'',
46614                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46615                f.itemCls||this.itemCls||'',
46616                f.width ? f.width + this.padWidth : 160 + this.padWidth
46617        ],true);
46618     }
46619 });
46620  
46621
46622 /**
46623  * @class Roo.form.FieldSet
46624  * @extends Roo.form.Layout
46625  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46626  * @constructor
46627  * @param {Object} config Configuration options
46628  */
46629 Roo.form.FieldSet = function(config){
46630     Roo.form.FieldSet.superclass.constructor.call(this, config);
46631 };
46632
46633 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46634     /**
46635      * @cfg {String} legend
46636      * The text to display as the legend for the FieldSet (defaults to '')
46637      */
46638     /**
46639      * @cfg {String/Object} autoCreate
46640      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46641      */
46642
46643     // private
46644     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46645
46646     // private
46647     onRender : function(ct, position){
46648         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46649         if(this.legend){
46650             this.setLegend(this.legend);
46651         }
46652     },
46653
46654     // private
46655     setLegend : function(text){
46656         if(this.rendered){
46657             this.el.child('legend').update(text);
46658         }
46659     }
46660 });/*
46661  * Based on:
46662  * Ext JS Library 1.1.1
46663  * Copyright(c) 2006-2007, Ext JS, LLC.
46664  *
46665  * Originally Released Under LGPL - original licence link has changed is not relivant.
46666  *
46667  * Fork - LGPL
46668  * <script type="text/javascript">
46669  */
46670 /**
46671  * @class Roo.form.VTypes
46672  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46673  * @singleton
46674  */
46675 Roo.form.VTypes = function(){
46676     // closure these in so they are only created once.
46677     var alpha = /^[a-zA-Z_]+$/;
46678     var alphanum = /^[a-zA-Z0-9_]+$/;
46679     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46680     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46681
46682     // All these messages and functions are configurable
46683     return {
46684         /**
46685          * The function used to validate email addresses
46686          * @param {String} value The email address
46687          */
46688         'email' : function(v){
46689             return email.test(v);
46690         },
46691         /**
46692          * The error text to display when the email validation function returns false
46693          * @type String
46694          */
46695         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46696         /**
46697          * The keystroke filter mask to be applied on email input
46698          * @type RegExp
46699          */
46700         'emailMask' : /[a-z0-9_\.\-@]/i,
46701
46702         /**
46703          * The function used to validate URLs
46704          * @param {String} value The URL
46705          */
46706         'url' : function(v){
46707             return url.test(v);
46708         },
46709         /**
46710          * The error text to display when the url validation function returns false
46711          * @type String
46712          */
46713         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46714         
46715         /**
46716          * The function used to validate alpha values
46717          * @param {String} value The value
46718          */
46719         'alpha' : function(v){
46720             return alpha.test(v);
46721         },
46722         /**
46723          * The error text to display when the alpha validation function returns false
46724          * @type String
46725          */
46726         'alphaText' : 'This field should only contain letters and _',
46727         /**
46728          * The keystroke filter mask to be applied on alpha input
46729          * @type RegExp
46730          */
46731         'alphaMask' : /[a-z_]/i,
46732
46733         /**
46734          * The function used to validate alphanumeric values
46735          * @param {String} value The value
46736          */
46737         'alphanum' : function(v){
46738             return alphanum.test(v);
46739         },
46740         /**
46741          * The error text to display when the alphanumeric validation function returns false
46742          * @type String
46743          */
46744         'alphanumText' : 'This field should only contain letters, numbers and _',
46745         /**
46746          * The keystroke filter mask to be applied on alphanumeric input
46747          * @type RegExp
46748          */
46749         'alphanumMask' : /[a-z0-9_]/i
46750     };
46751 }();//<script type="text/javascript">
46752
46753 /**
46754  * @class Roo.form.FCKeditor
46755  * @extends Roo.form.TextArea
46756  * Wrapper around the FCKEditor http://www.fckeditor.net
46757  * @constructor
46758  * Creates a new FCKeditor
46759  * @param {Object} config Configuration options
46760  */
46761 Roo.form.FCKeditor = function(config){
46762     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46763     this.addEvents({
46764          /**
46765          * @event editorinit
46766          * Fired when the editor is initialized - you can add extra handlers here..
46767          * @param {FCKeditor} this
46768          * @param {Object} the FCK object.
46769          */
46770         editorinit : true
46771     });
46772     
46773     
46774 };
46775 Roo.form.FCKeditor.editors = { };
46776 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46777 {
46778     //defaultAutoCreate : {
46779     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46780     //},
46781     // private
46782     /**
46783      * @cfg {Object} fck options - see fck manual for details.
46784      */
46785     fckconfig : false,
46786     
46787     /**
46788      * @cfg {Object} fck toolbar set (Basic or Default)
46789      */
46790     toolbarSet : 'Basic',
46791     /**
46792      * @cfg {Object} fck BasePath
46793      */ 
46794     basePath : '/fckeditor/',
46795     
46796     
46797     frame : false,
46798     
46799     value : '',
46800     
46801    
46802     onRender : function(ct, position)
46803     {
46804         if(!this.el){
46805             this.defaultAutoCreate = {
46806                 tag: "textarea",
46807                 style:"width:300px;height:60px;",
46808                 autocomplete: "new-password"
46809             };
46810         }
46811         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46812         /*
46813         if(this.grow){
46814             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46815             if(this.preventScrollbars){
46816                 this.el.setStyle("overflow", "hidden");
46817             }
46818             this.el.setHeight(this.growMin);
46819         }
46820         */
46821         //console.log('onrender' + this.getId() );
46822         Roo.form.FCKeditor.editors[this.getId()] = this;
46823          
46824
46825         this.replaceTextarea() ;
46826         
46827     },
46828     
46829     getEditor : function() {
46830         return this.fckEditor;
46831     },
46832     /**
46833      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46834      * @param {Mixed} value The value to set
46835      */
46836     
46837     
46838     setValue : function(value)
46839     {
46840         //console.log('setValue: ' + value);
46841         
46842         if(typeof(value) == 'undefined') { // not sure why this is happending...
46843             return;
46844         }
46845         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46846         
46847         //if(!this.el || !this.getEditor()) {
46848         //    this.value = value;
46849             //this.setValue.defer(100,this,[value]);    
46850         //    return;
46851         //} 
46852         
46853         if(!this.getEditor()) {
46854             return;
46855         }
46856         
46857         this.getEditor().SetData(value);
46858         
46859         //
46860
46861     },
46862
46863     /**
46864      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46865      * @return {Mixed} value The field value
46866      */
46867     getValue : function()
46868     {
46869         
46870         if (this.frame && this.frame.dom.style.display == 'none') {
46871             return Roo.form.FCKeditor.superclass.getValue.call(this);
46872         }
46873         
46874         if(!this.el || !this.getEditor()) {
46875            
46876            // this.getValue.defer(100,this); 
46877             return this.value;
46878         }
46879        
46880         
46881         var value=this.getEditor().GetData();
46882         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46883         return Roo.form.FCKeditor.superclass.getValue.call(this);
46884         
46885
46886     },
46887
46888     /**
46889      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46890      * @return {Mixed} value The field value
46891      */
46892     getRawValue : function()
46893     {
46894         if (this.frame && this.frame.dom.style.display == 'none') {
46895             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46896         }
46897         
46898         if(!this.el || !this.getEditor()) {
46899             //this.getRawValue.defer(100,this); 
46900             return this.value;
46901             return;
46902         }
46903         
46904         
46905         
46906         var value=this.getEditor().GetData();
46907         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46908         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46909          
46910     },
46911     
46912     setSize : function(w,h) {
46913         
46914         
46915         
46916         //if (this.frame && this.frame.dom.style.display == 'none') {
46917         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46918         //    return;
46919         //}
46920         //if(!this.el || !this.getEditor()) {
46921         //    this.setSize.defer(100,this, [w,h]); 
46922         //    return;
46923         //}
46924         
46925         
46926         
46927         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46928         
46929         this.frame.dom.setAttribute('width', w);
46930         this.frame.dom.setAttribute('height', h);
46931         this.frame.setSize(w,h);
46932         
46933     },
46934     
46935     toggleSourceEdit : function(value) {
46936         
46937       
46938          
46939         this.el.dom.style.display = value ? '' : 'none';
46940         this.frame.dom.style.display = value ?  'none' : '';
46941         
46942     },
46943     
46944     
46945     focus: function(tag)
46946     {
46947         if (this.frame.dom.style.display == 'none') {
46948             return Roo.form.FCKeditor.superclass.focus.call(this);
46949         }
46950         if(!this.el || !this.getEditor()) {
46951             this.focus.defer(100,this, [tag]); 
46952             return;
46953         }
46954         
46955         
46956         
46957         
46958         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46959         this.getEditor().Focus();
46960         if (tgs.length) {
46961             if (!this.getEditor().Selection.GetSelection()) {
46962                 this.focus.defer(100,this, [tag]); 
46963                 return;
46964             }
46965             
46966             
46967             var r = this.getEditor().EditorDocument.createRange();
46968             r.setStart(tgs[0],0);
46969             r.setEnd(tgs[0],0);
46970             this.getEditor().Selection.GetSelection().removeAllRanges();
46971             this.getEditor().Selection.GetSelection().addRange(r);
46972             this.getEditor().Focus();
46973         }
46974         
46975     },
46976     
46977     
46978     
46979     replaceTextarea : function()
46980     {
46981         if ( document.getElementById( this.getId() + '___Frame' ) )
46982             return ;
46983         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46984         //{
46985             // We must check the elements firstly using the Id and then the name.
46986         var oTextarea = document.getElementById( this.getId() );
46987         
46988         var colElementsByName = document.getElementsByName( this.getId() ) ;
46989          
46990         oTextarea.style.display = 'none' ;
46991
46992         if ( oTextarea.tabIndex ) {            
46993             this.TabIndex = oTextarea.tabIndex ;
46994         }
46995         
46996         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46997         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46998         this.frame = Roo.get(this.getId() + '___Frame')
46999     },
47000     
47001     _getConfigHtml : function()
47002     {
47003         var sConfig = '' ;
47004
47005         for ( var o in this.fckconfig ) {
47006             sConfig += sConfig.length > 0  ? '&amp;' : '';
47007             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47008         }
47009
47010         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47011     },
47012     
47013     
47014     _getIFrameHtml : function()
47015     {
47016         var sFile = 'fckeditor.html' ;
47017         /* no idea what this is about..
47018         try
47019         {
47020             if ( (/fcksource=true/i).test( window.top.location.search ) )
47021                 sFile = 'fckeditor.original.html' ;
47022         }
47023         catch (e) { 
47024         */
47025
47026         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47027         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47028         
47029         
47030         var html = '<iframe id="' + this.getId() +
47031             '___Frame" src="' + sLink +
47032             '" width="' + this.width +
47033             '" height="' + this.height + '"' +
47034             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47035             ' frameborder="0" scrolling="no"></iframe>' ;
47036
47037         return html ;
47038     },
47039     
47040     _insertHtmlBefore : function( html, element )
47041     {
47042         if ( element.insertAdjacentHTML )       {
47043             // IE
47044             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47045         } else { // Gecko
47046             var oRange = document.createRange() ;
47047             oRange.setStartBefore( element ) ;
47048             var oFragment = oRange.createContextualFragment( html );
47049             element.parentNode.insertBefore( oFragment, element ) ;
47050         }
47051     }
47052     
47053     
47054   
47055     
47056     
47057     
47058     
47059
47060 });
47061
47062 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47063
47064 function FCKeditor_OnComplete(editorInstance){
47065     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47066     f.fckEditor = editorInstance;
47067     //console.log("loaded");
47068     f.fireEvent('editorinit', f, editorInstance);
47069
47070   
47071
47072  
47073
47074
47075
47076
47077
47078
47079
47080
47081
47082
47083
47084
47085
47086
47087
47088 //<script type="text/javascript">
47089 /**
47090  * @class Roo.form.GridField
47091  * @extends Roo.form.Field
47092  * Embed a grid (or editable grid into a form)
47093  * STATUS ALPHA
47094  * 
47095  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47096  * it needs 
47097  * xgrid.store = Roo.data.Store
47098  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47099  * xgrid.store.reader = Roo.data.JsonReader 
47100  * 
47101  * 
47102  * @constructor
47103  * Creates a new GridField
47104  * @param {Object} config Configuration options
47105  */
47106 Roo.form.GridField = function(config){
47107     Roo.form.GridField.superclass.constructor.call(this, config);
47108      
47109 };
47110
47111 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47112     /**
47113      * @cfg {Number} width  - used to restrict width of grid..
47114      */
47115     width : 100,
47116     /**
47117      * @cfg {Number} height - used to restrict height of grid..
47118      */
47119     height : 50,
47120      /**
47121      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47122          * 
47123          *}
47124      */
47125     xgrid : false, 
47126     /**
47127      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47128      * {tag: "input", type: "checkbox", autocomplete: "off"})
47129      */
47130    // defaultAutoCreate : { tag: 'div' },
47131     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47132     /**
47133      * @cfg {String} addTitle Text to include for adding a title.
47134      */
47135     addTitle : false,
47136     //
47137     onResize : function(){
47138         Roo.form.Field.superclass.onResize.apply(this, arguments);
47139     },
47140
47141     initEvents : function(){
47142         // Roo.form.Checkbox.superclass.initEvents.call(this);
47143         // has no events...
47144        
47145     },
47146
47147
47148     getResizeEl : function(){
47149         return this.wrap;
47150     },
47151
47152     getPositionEl : function(){
47153         return this.wrap;
47154     },
47155
47156     // private
47157     onRender : function(ct, position){
47158         
47159         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47160         var style = this.style;
47161         delete this.style;
47162         
47163         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47164         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47165         this.viewEl = this.wrap.createChild({ tag: 'div' });
47166         if (style) {
47167             this.viewEl.applyStyles(style);
47168         }
47169         if (this.width) {
47170             this.viewEl.setWidth(this.width);
47171         }
47172         if (this.height) {
47173             this.viewEl.setHeight(this.height);
47174         }
47175         //if(this.inputValue !== undefined){
47176         //this.setValue(this.value);
47177         
47178         
47179         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47180         
47181         
47182         this.grid.render();
47183         this.grid.getDataSource().on('remove', this.refreshValue, this);
47184         this.grid.getDataSource().on('update', this.refreshValue, this);
47185         this.grid.on('afteredit', this.refreshValue, this);
47186  
47187     },
47188      
47189     
47190     /**
47191      * Sets the value of the item. 
47192      * @param {String} either an object  or a string..
47193      */
47194     setValue : function(v){
47195         //this.value = v;
47196         v = v || []; // empty set..
47197         // this does not seem smart - it really only affects memoryproxy grids..
47198         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47199             var ds = this.grid.getDataSource();
47200             // assumes a json reader..
47201             var data = {}
47202             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47203             ds.loadData( data);
47204         }
47205         // clear selection so it does not get stale.
47206         if (this.grid.sm) { 
47207             this.grid.sm.clearSelections();
47208         }
47209         
47210         Roo.form.GridField.superclass.setValue.call(this, v);
47211         this.refreshValue();
47212         // should load data in the grid really....
47213     },
47214     
47215     // private
47216     refreshValue: function() {
47217          var val = [];
47218         this.grid.getDataSource().each(function(r) {
47219             val.push(r.data);
47220         });
47221         this.el.dom.value = Roo.encode(val);
47222     }
47223     
47224      
47225     
47226     
47227 });/*
47228  * Based on:
47229  * Ext JS Library 1.1.1
47230  * Copyright(c) 2006-2007, Ext JS, LLC.
47231  *
47232  * Originally Released Under LGPL - original licence link has changed is not relivant.
47233  *
47234  * Fork - LGPL
47235  * <script type="text/javascript">
47236  */
47237 /**
47238  * @class Roo.form.DisplayField
47239  * @extends Roo.form.Field
47240  * A generic Field to display non-editable data.
47241  * @constructor
47242  * Creates a new Display Field item.
47243  * @param {Object} config Configuration options
47244  */
47245 Roo.form.DisplayField = function(config){
47246     Roo.form.DisplayField.superclass.constructor.call(this, config);
47247     
47248 };
47249
47250 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47251     inputType:      'hidden',
47252     allowBlank:     true,
47253     readOnly:         true,
47254     
47255  
47256     /**
47257      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47258      */
47259     focusClass : undefined,
47260     /**
47261      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47262      */
47263     fieldClass: 'x-form-field',
47264     
47265      /**
47266      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47267      */
47268     valueRenderer: undefined,
47269     
47270     width: 100,
47271     /**
47272      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47273      * {tag: "input", type: "checkbox", autocomplete: "off"})
47274      */
47275      
47276  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47277
47278     onResize : function(){
47279         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47280         
47281     },
47282
47283     initEvents : function(){
47284         // Roo.form.Checkbox.superclass.initEvents.call(this);
47285         // has no events...
47286        
47287     },
47288
47289
47290     getResizeEl : function(){
47291         return this.wrap;
47292     },
47293
47294     getPositionEl : function(){
47295         return this.wrap;
47296     },
47297
47298     // private
47299     onRender : function(ct, position){
47300         
47301         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47302         //if(this.inputValue !== undefined){
47303         this.wrap = this.el.wrap();
47304         
47305         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47306         
47307         if (this.bodyStyle) {
47308             this.viewEl.applyStyles(this.bodyStyle);
47309         }
47310         //this.viewEl.setStyle('padding', '2px');
47311         
47312         this.setValue(this.value);
47313         
47314     },
47315 /*
47316     // private
47317     initValue : Roo.emptyFn,
47318
47319   */
47320
47321         // private
47322     onClick : function(){
47323         
47324     },
47325
47326     /**
47327      * Sets the checked state of the checkbox.
47328      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47329      */
47330     setValue : function(v){
47331         this.value = v;
47332         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47333         // this might be called before we have a dom element..
47334         if (!this.viewEl) {
47335             return;
47336         }
47337         this.viewEl.dom.innerHTML = html;
47338         Roo.form.DisplayField.superclass.setValue.call(this, v);
47339
47340     }
47341 });/*
47342  * 
47343  * Licence- LGPL
47344  * 
47345  */
47346
47347 /**
47348  * @class Roo.form.DayPicker
47349  * @extends Roo.form.Field
47350  * A Day picker show [M] [T] [W] ....
47351  * @constructor
47352  * Creates a new Day Picker
47353  * @param {Object} config Configuration options
47354  */
47355 Roo.form.DayPicker= function(config){
47356     Roo.form.DayPicker.superclass.constructor.call(this, config);
47357      
47358 };
47359
47360 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47361     /**
47362      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47363      */
47364     focusClass : undefined,
47365     /**
47366      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47367      */
47368     fieldClass: "x-form-field",
47369    
47370     /**
47371      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47372      * {tag: "input", type: "checkbox", autocomplete: "off"})
47373      */
47374     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47375     
47376    
47377     actionMode : 'viewEl', 
47378     //
47379     // private
47380  
47381     inputType : 'hidden',
47382     
47383      
47384     inputElement: false, // real input element?
47385     basedOn: false, // ????
47386     
47387     isFormField: true, // not sure where this is needed!!!!
47388
47389     onResize : function(){
47390         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47391         if(!this.boxLabel){
47392             this.el.alignTo(this.wrap, 'c-c');
47393         }
47394     },
47395
47396     initEvents : function(){
47397         Roo.form.Checkbox.superclass.initEvents.call(this);
47398         this.el.on("click", this.onClick,  this);
47399         this.el.on("change", this.onClick,  this);
47400     },
47401
47402
47403     getResizeEl : function(){
47404         return this.wrap;
47405     },
47406
47407     getPositionEl : function(){
47408         return this.wrap;
47409     },
47410
47411     
47412     // private
47413     onRender : function(ct, position){
47414         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47415        
47416         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47417         
47418         var r1 = '<table><tr>';
47419         var r2 = '<tr class="x-form-daypick-icons">';
47420         for (var i=0; i < 7; i++) {
47421             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47422             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47423         }
47424         
47425         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47426         viewEl.select('img').on('click', this.onClick, this);
47427         this.viewEl = viewEl;   
47428         
47429         
47430         // this will not work on Chrome!!!
47431         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47432         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47433         
47434         
47435           
47436
47437     },
47438
47439     // private
47440     initValue : Roo.emptyFn,
47441
47442     /**
47443      * Returns the checked state of the checkbox.
47444      * @return {Boolean} True if checked, else false
47445      */
47446     getValue : function(){
47447         return this.el.dom.value;
47448         
47449     },
47450
47451         // private
47452     onClick : function(e){ 
47453         //this.setChecked(!this.checked);
47454         Roo.get(e.target).toggleClass('x-menu-item-checked');
47455         this.refreshValue();
47456         //if(this.el.dom.checked != this.checked){
47457         //    this.setValue(this.el.dom.checked);
47458        // }
47459     },
47460     
47461     // private
47462     refreshValue : function()
47463     {
47464         var val = '';
47465         this.viewEl.select('img',true).each(function(e,i,n)  {
47466             val += e.is(".x-menu-item-checked") ? String(n) : '';
47467         });
47468         this.setValue(val, true);
47469     },
47470
47471     /**
47472      * Sets the checked state of the checkbox.
47473      * On is always based on a string comparison between inputValue and the param.
47474      * @param {Boolean/String} value - the value to set 
47475      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47476      */
47477     setValue : function(v,suppressEvent){
47478         if (!this.el.dom) {
47479             return;
47480         }
47481         var old = this.el.dom.value ;
47482         this.el.dom.value = v;
47483         if (suppressEvent) {
47484             return ;
47485         }
47486          
47487         // update display..
47488         this.viewEl.select('img',true).each(function(e,i,n)  {
47489             
47490             var on = e.is(".x-menu-item-checked");
47491             var newv = v.indexOf(String(n)) > -1;
47492             if (on != newv) {
47493                 e.toggleClass('x-menu-item-checked');
47494             }
47495             
47496         });
47497         
47498         
47499         this.fireEvent('change', this, v, old);
47500         
47501         
47502     },
47503    
47504     // handle setting of hidden value by some other method!!?!?
47505     setFromHidden: function()
47506     {
47507         if(!this.el){
47508             return;
47509         }
47510         //console.log("SET FROM HIDDEN");
47511         //alert('setFrom hidden');
47512         this.setValue(this.el.dom.value);
47513     },
47514     
47515     onDestroy : function()
47516     {
47517         if(this.viewEl){
47518             Roo.get(this.viewEl).remove();
47519         }
47520          
47521         Roo.form.DayPicker.superclass.onDestroy.call(this);
47522     }
47523
47524 });/*
47525  * RooJS Library 1.1.1
47526  * Copyright(c) 2008-2011  Alan Knowles
47527  *
47528  * License - LGPL
47529  */
47530  
47531
47532 /**
47533  * @class Roo.form.ComboCheck
47534  * @extends Roo.form.ComboBox
47535  * A combobox for multiple select items.
47536  *
47537  * FIXME - could do with a reset button..
47538  * 
47539  * @constructor
47540  * Create a new ComboCheck
47541  * @param {Object} config Configuration options
47542  */
47543 Roo.form.ComboCheck = function(config){
47544     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47545     // should verify some data...
47546     // like
47547     // hiddenName = required..
47548     // displayField = required
47549     // valudField == required
47550     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47551     var _t = this;
47552     Roo.each(req, function(e) {
47553         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47554             throw "Roo.form.ComboCheck : missing value for: " + e;
47555         }
47556     });
47557     
47558     
47559 };
47560
47561 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47562      
47563      
47564     editable : false,
47565      
47566     selectedClass: 'x-menu-item-checked', 
47567     
47568     // private
47569     onRender : function(ct, position){
47570         var _t = this;
47571         
47572         
47573         
47574         if(!this.tpl){
47575             var cls = 'x-combo-list';
47576
47577             
47578             this.tpl =  new Roo.Template({
47579                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47580                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47581                    '<span>{' + this.displayField + '}</span>' +
47582                     '</div>' 
47583                 
47584             });
47585         }
47586  
47587         
47588         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47589         this.view.singleSelect = false;
47590         this.view.multiSelect = true;
47591         this.view.toggleSelect = true;
47592         this.pageTb.add(new Roo.Toolbar.Fill(), {
47593             
47594             text: 'Done',
47595             handler: function()
47596             {
47597                 _t.collapse();
47598             }
47599         });
47600     },
47601     
47602     onViewOver : function(e, t){
47603         // do nothing...
47604         return;
47605         
47606     },
47607     
47608     onViewClick : function(doFocus,index){
47609         return;
47610         
47611     },
47612     select: function () {
47613         //Roo.log("SELECT CALLED");
47614     },
47615      
47616     selectByValue : function(xv, scrollIntoView){
47617         var ar = this.getValueArray();
47618         var sels = [];
47619         
47620         Roo.each(ar, function(v) {
47621             if(v === undefined || v === null){
47622                 return;
47623             }
47624             var r = this.findRecord(this.valueField, v);
47625             if(r){
47626                 sels.push(this.store.indexOf(r))
47627                 
47628             }
47629         },this);
47630         this.view.select(sels);
47631         return false;
47632     },
47633     
47634     
47635     
47636     onSelect : function(record, index){
47637        // Roo.log("onselect Called");
47638        // this is only called by the clear button now..
47639         this.view.clearSelections();
47640         this.setValue('[]');
47641         if (this.value != this.valueBefore) {
47642             this.fireEvent('change', this, this.value, this.valueBefore);
47643             this.valueBefore = this.value;
47644         }
47645     },
47646     getValueArray : function()
47647     {
47648         var ar = [] ;
47649         
47650         try {
47651             //Roo.log(this.value);
47652             if (typeof(this.value) == 'undefined') {
47653                 return [];
47654             }
47655             var ar = Roo.decode(this.value);
47656             return  ar instanceof Array ? ar : []; //?? valid?
47657             
47658         } catch(e) {
47659             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47660             return [];
47661         }
47662          
47663     },
47664     expand : function ()
47665     {
47666         
47667         Roo.form.ComboCheck.superclass.expand.call(this);
47668         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47669         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47670         
47671
47672     },
47673     
47674     collapse : function(){
47675         Roo.form.ComboCheck.superclass.collapse.call(this);
47676         var sl = this.view.getSelectedIndexes();
47677         var st = this.store;
47678         var nv = [];
47679         var tv = [];
47680         var r;
47681         Roo.each(sl, function(i) {
47682             r = st.getAt(i);
47683             nv.push(r.get(this.valueField));
47684         },this);
47685         this.setValue(Roo.encode(nv));
47686         if (this.value != this.valueBefore) {
47687
47688             this.fireEvent('change', this, this.value, this.valueBefore);
47689             this.valueBefore = this.value;
47690         }
47691         
47692     },
47693     
47694     setValue : function(v){
47695         // Roo.log(v);
47696         this.value = v;
47697         
47698         var vals = this.getValueArray();
47699         var tv = [];
47700         Roo.each(vals, function(k) {
47701             var r = this.findRecord(this.valueField, k);
47702             if(r){
47703                 tv.push(r.data[this.displayField]);
47704             }else if(this.valueNotFoundText !== undefined){
47705                 tv.push( this.valueNotFoundText );
47706             }
47707         },this);
47708        // Roo.log(tv);
47709         
47710         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47711         this.hiddenField.value = v;
47712         this.value = v;
47713     }
47714     
47715 });/*
47716  * Based on:
47717  * Ext JS Library 1.1.1
47718  * Copyright(c) 2006-2007, Ext JS, LLC.
47719  *
47720  * Originally Released Under LGPL - original licence link has changed is not relivant.
47721  *
47722  * Fork - LGPL
47723  * <script type="text/javascript">
47724  */
47725  
47726 /**
47727  * @class Roo.form.Signature
47728  * @extends Roo.form.Field
47729  * Signature field.  
47730  * @constructor
47731  * 
47732  * @param {Object} config Configuration options
47733  */
47734
47735 Roo.form.Signature = function(config){
47736     Roo.form.Signature.superclass.constructor.call(this, config);
47737     
47738     this.addEvents({// not in used??
47739          /**
47740          * @event confirm
47741          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47742              * @param {Roo.form.Signature} combo This combo box
47743              */
47744         'confirm' : true,
47745         /**
47746          * @event reset
47747          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47748              * @param {Roo.form.ComboBox} combo This combo box
47749              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47750              */
47751         'reset' : true
47752     });
47753 };
47754
47755 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47756     /**
47757      * @cfg {Object} labels Label to use when rendering a form.
47758      * defaults to 
47759      * labels : { 
47760      *      clear : "Clear",
47761      *      confirm : "Confirm"
47762      *  }
47763      */
47764     labels : { 
47765         clear : "Clear",
47766         confirm : "Confirm"
47767     },
47768     /**
47769      * @cfg {Number} width The signature panel width (defaults to 300)
47770      */
47771     width: 300,
47772     /**
47773      * @cfg {Number} height The signature panel height (defaults to 100)
47774      */
47775     height : 100,
47776     /**
47777      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47778      */
47779     allowBlank : false,
47780     
47781     //private
47782     // {Object} signPanel The signature SVG panel element (defaults to {})
47783     signPanel : {},
47784     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47785     isMouseDown : false,
47786     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47787     isConfirmed : false,
47788     // {String} signatureTmp SVG mapping string (defaults to empty string)
47789     signatureTmp : '',
47790     
47791     
47792     defaultAutoCreate : { // modified by initCompnoent..
47793         tag: "input",
47794         type:"hidden"
47795     },
47796
47797     // private
47798     onRender : function(ct, position){
47799         
47800         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47801         
47802         this.wrap = this.el.wrap({
47803             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47804         });
47805         
47806         this.createToolbar(this);
47807         this.signPanel = this.wrap.createChild({
47808                 tag: 'div',
47809                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47810             }, this.el
47811         );
47812             
47813         this.svgID = Roo.id();
47814         this.svgEl = this.signPanel.createChild({
47815               xmlns : 'http://www.w3.org/2000/svg',
47816               tag : 'svg',
47817               id : this.svgID + "-svg",
47818               width: this.width,
47819               height: this.height,
47820               viewBox: '0 0 '+this.width+' '+this.height,
47821               cn : [
47822                 {
47823                     tag: "rect",
47824                     id: this.svgID + "-svg-r",
47825                     width: this.width,
47826                     height: this.height,
47827                     fill: "#ffa"
47828                 },
47829                 {
47830                     tag: "line",
47831                     id: this.svgID + "-svg-l",
47832                     x1: "0", // start
47833                     y1: (this.height*0.8), // start set the line in 80% of height
47834                     x2: this.width, // end
47835                     y2: (this.height*0.8), // end set the line in 80% of height
47836                     'stroke': "#666",
47837                     'stroke-width': "1",
47838                     'stroke-dasharray': "3",
47839                     'shape-rendering': "crispEdges",
47840                     'pointer-events': "none"
47841                 },
47842                 {
47843                     tag: "path",
47844                     id: this.svgID + "-svg-p",
47845                     'stroke': "navy",
47846                     'stroke-width': "3",
47847                     'fill': "none",
47848                     'pointer-events': 'none'
47849                 }
47850               ]
47851         });
47852         this.createSVG();
47853         this.svgBox = this.svgEl.dom.getScreenCTM();
47854     },
47855     createSVG : function(){ 
47856         var svg = this.signPanel;
47857         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47858         var t = this;
47859
47860         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47861         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47862         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47863         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47864         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47865         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47866         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47867         
47868     },
47869     isTouchEvent : function(e){
47870         return e.type.match(/^touch/);
47871     },
47872     getCoords : function (e) {
47873         var pt    = this.svgEl.dom.createSVGPoint();
47874         pt.x = e.clientX; 
47875         pt.y = e.clientY;
47876         if (this.isTouchEvent(e)) {
47877             pt.x =  e.targetTouches[0].clientX 
47878             pt.y = e.targetTouches[0].clientY;
47879         }
47880         var a = this.svgEl.dom.getScreenCTM();
47881         var b = a.inverse();
47882         var mx = pt.matrixTransform(b);
47883         return mx.x + ',' + mx.y;
47884     },
47885     //mouse event headler 
47886     down : function (e) {
47887         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47888         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47889         
47890         this.isMouseDown = true;
47891         
47892         e.preventDefault();
47893     },
47894     move : function (e) {
47895         if (this.isMouseDown) {
47896             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47897             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47898         }
47899         
47900         e.preventDefault();
47901     },
47902     up : function (e) {
47903         this.isMouseDown = false;
47904         var sp = this.signatureTmp.split(' ');
47905         
47906         if(sp.length > 1){
47907             if(!sp[sp.length-2].match(/^L/)){
47908                 sp.pop();
47909                 sp.pop();
47910                 sp.push("");
47911                 this.signatureTmp = sp.join(" ");
47912             }
47913         }
47914         if(this.getValue() != this.signatureTmp){
47915             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47916             this.isConfirmed = false;
47917         }
47918         e.preventDefault();
47919     },
47920     
47921     /**
47922      * Protected method that will not generally be called directly. It
47923      * is called when the editor creates its toolbar. Override this method if you need to
47924      * add custom toolbar buttons.
47925      * @param {HtmlEditor} editor
47926      */
47927     createToolbar : function(editor){
47928          function btn(id, toggle, handler){
47929             var xid = fid + '-'+ id ;
47930             return {
47931                 id : xid,
47932                 cmd : id,
47933                 cls : 'x-btn-icon x-edit-'+id,
47934                 enableToggle:toggle !== false,
47935                 scope: editor, // was editor...
47936                 handler:handler||editor.relayBtnCmd,
47937                 clickEvent:'mousedown',
47938                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47939                 tabIndex:-1
47940             };
47941         }
47942         
47943         
47944         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47945         this.tb = tb;
47946         this.tb.add(
47947            {
47948                 cls : ' x-signature-btn x-signature-'+id,
47949                 scope: editor, // was editor...
47950                 handler: this.reset,
47951                 clickEvent:'mousedown',
47952                 text: this.labels.clear
47953             },
47954             {
47955                  xtype : 'Fill',
47956                  xns: Roo.Toolbar
47957             }, 
47958             {
47959                 cls : '  x-signature-btn x-signature-'+id,
47960                 scope: editor, // was editor...
47961                 handler: this.confirmHandler,
47962                 clickEvent:'mousedown',
47963                 text: this.labels.confirm
47964             }
47965         );
47966     
47967     },
47968     //public
47969     /**
47970      * when user is clicked confirm then show this image.....
47971      * 
47972      * @return {String} Image Data URI
47973      */
47974     getImageDataURI : function(){
47975         var svg = this.svgEl.dom.parentNode.innerHTML;
47976         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47977         return src; 
47978     },
47979     /**
47980      * 
47981      * @return {Boolean} this.isConfirmed
47982      */
47983     getConfirmed : function(){
47984         return this.isConfirmed;
47985     },
47986     /**
47987      * 
47988      * @return {Number} this.width
47989      */
47990     getWidth : function(){
47991         return this.width;
47992     },
47993     /**
47994      * 
47995      * @return {Number} this.height
47996      */
47997     getHeight : function(){
47998         return this.height;
47999     },
48000     // private
48001     getSignature : function(){
48002         return this.signatureTmp;
48003     },
48004     // private
48005     reset : function(){
48006         this.signatureTmp = '';
48007         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48008         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48009         this.isConfirmed = false;
48010         Roo.form.Signature.superclass.reset.call(this);
48011     },
48012     setSignature : function(s){
48013         this.signatureTmp = s;
48014         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48015         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48016         this.setValue(s);
48017         this.isConfirmed = false;
48018         Roo.form.Signature.superclass.reset.call(this);
48019     }, 
48020     test : function(){
48021 //        Roo.log(this.signPanel.dom.contentWindow.up())
48022     },
48023     //private
48024     setConfirmed : function(){
48025         
48026         
48027         
48028 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48029     },
48030     // private
48031     confirmHandler : function(){
48032         if(!this.getSignature()){
48033             return;
48034         }
48035         
48036         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48037         this.setValue(this.getSignature());
48038         this.isConfirmed = true;
48039         
48040         this.fireEvent('confirm', this);
48041     },
48042     // private
48043     // Subclasses should provide the validation implementation by overriding this
48044     validateValue : function(value){
48045         if(this.allowBlank){
48046             return true;
48047         }
48048         
48049         if(this.isConfirmed){
48050             return true;
48051         }
48052         return false;
48053     }
48054 });/*
48055  * Based on:
48056  * Ext JS Library 1.1.1
48057  * Copyright(c) 2006-2007, Ext JS, LLC.
48058  *
48059  * Originally Released Under LGPL - original licence link has changed is not relivant.
48060  *
48061  * Fork - LGPL
48062  * <script type="text/javascript">
48063  */
48064  
48065
48066 /**
48067  * @class Roo.form.ComboBox
48068  * @extends Roo.form.TriggerField
48069  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48070  * @constructor
48071  * Create a new ComboBox.
48072  * @param {Object} config Configuration options
48073  */
48074 Roo.form.Select = function(config){
48075     Roo.form.Select.superclass.constructor.call(this, config);
48076      
48077 };
48078
48079 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48080     /**
48081      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48082      */
48083     /**
48084      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48085      * rendering into an Roo.Editor, defaults to false)
48086      */
48087     /**
48088      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48089      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48090      */
48091     /**
48092      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48093      */
48094     /**
48095      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48096      * the dropdown list (defaults to undefined, with no header element)
48097      */
48098
48099      /**
48100      * @cfg {String/Roo.Template} tpl The template to use to render the output
48101      */
48102      
48103     // private
48104     defaultAutoCreate : {tag: "select"  },
48105     /**
48106      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48107      */
48108     listWidth: undefined,
48109     /**
48110      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48111      * mode = 'remote' or 'text' if mode = 'local')
48112      */
48113     displayField: undefined,
48114     /**
48115      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48116      * mode = 'remote' or 'value' if mode = 'local'). 
48117      * Note: use of a valueField requires the user make a selection
48118      * in order for a value to be mapped.
48119      */
48120     valueField: undefined,
48121     
48122     
48123     /**
48124      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48125      * field's data value (defaults to the underlying DOM element's name)
48126      */
48127     hiddenName: undefined,
48128     /**
48129      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48130      */
48131     listClass: '',
48132     /**
48133      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48134      */
48135     selectedClass: 'x-combo-selected',
48136     /**
48137      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48138      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48139      * which displays a downward arrow icon).
48140      */
48141     triggerClass : 'x-form-arrow-trigger',
48142     /**
48143      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48144      */
48145     shadow:'sides',
48146     /**
48147      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48148      * anchor positions (defaults to 'tl-bl')
48149      */
48150     listAlign: 'tl-bl?',
48151     /**
48152      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48153      */
48154     maxHeight: 300,
48155     /**
48156      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48157      * query specified by the allQuery config option (defaults to 'query')
48158      */
48159     triggerAction: 'query',
48160     /**
48161      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48162      * (defaults to 4, does not apply if editable = false)
48163      */
48164     minChars : 4,
48165     /**
48166      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48167      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48168      */
48169     typeAhead: false,
48170     /**
48171      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48172      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48173      */
48174     queryDelay: 500,
48175     /**
48176      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48177      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48178      */
48179     pageSize: 0,
48180     /**
48181      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48182      * when editable = true (defaults to false)
48183      */
48184     selectOnFocus:false,
48185     /**
48186      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48187      */
48188     queryParam: 'query',
48189     /**
48190      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48191      * when mode = 'remote' (defaults to 'Loading...')
48192      */
48193     loadingText: 'Loading...',
48194     /**
48195      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48196      */
48197     resizable: false,
48198     /**
48199      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48200      */
48201     handleHeight : 8,
48202     /**
48203      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48204      * traditional select (defaults to true)
48205      */
48206     editable: true,
48207     /**
48208      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48209      */
48210     allQuery: '',
48211     /**
48212      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48213      */
48214     mode: 'remote',
48215     /**
48216      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48217      * listWidth has a higher value)
48218      */
48219     minListWidth : 70,
48220     /**
48221      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48222      * allow the user to set arbitrary text into the field (defaults to false)
48223      */
48224     forceSelection:false,
48225     /**
48226      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48227      * if typeAhead = true (defaults to 250)
48228      */
48229     typeAheadDelay : 250,
48230     /**
48231      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48232      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48233      */
48234     valueNotFoundText : undefined,
48235     
48236     /**
48237      * @cfg {String} defaultValue The value displayed after loading the store.
48238      */
48239     defaultValue: '',
48240     
48241     /**
48242      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48243      */
48244     blockFocus : false,
48245     
48246     /**
48247      * @cfg {Boolean} disableClear Disable showing of clear button.
48248      */
48249     disableClear : false,
48250     /**
48251      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48252      */
48253     alwaysQuery : false,
48254     
48255     //private
48256     addicon : false,
48257     editicon: false,
48258     
48259     // element that contains real text value.. (when hidden is used..)
48260      
48261     // private
48262     onRender : function(ct, position){
48263         Roo.form.Field.prototype.onRender.call(this, ct, position);
48264         
48265         if(this.store){
48266             this.store.on('beforeload', this.onBeforeLoad, this);
48267             this.store.on('load', this.onLoad, this);
48268             this.store.on('loadexception', this.onLoadException, this);
48269             this.store.load({});
48270         }
48271         
48272         
48273         
48274     },
48275
48276     // private
48277     initEvents : function(){
48278         //Roo.form.ComboBox.superclass.initEvents.call(this);
48279  
48280     },
48281
48282     onDestroy : function(){
48283        
48284         if(this.store){
48285             this.store.un('beforeload', this.onBeforeLoad, this);
48286             this.store.un('load', this.onLoad, this);
48287             this.store.un('loadexception', this.onLoadException, this);
48288         }
48289         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48290     },
48291
48292     // private
48293     fireKey : function(e){
48294         if(e.isNavKeyPress() && !this.list.isVisible()){
48295             this.fireEvent("specialkey", this, e);
48296         }
48297     },
48298
48299     // private
48300     onResize: function(w, h){
48301         
48302         return; 
48303     
48304         
48305     },
48306
48307     /**
48308      * Allow or prevent the user from directly editing the field text.  If false is passed,
48309      * the user will only be able to select from the items defined in the dropdown list.  This method
48310      * is the runtime equivalent of setting the 'editable' config option at config time.
48311      * @param {Boolean} value True to allow the user to directly edit the field text
48312      */
48313     setEditable : function(value){
48314          
48315     },
48316
48317     // private
48318     onBeforeLoad : function(){
48319         
48320         Roo.log("Select before load");
48321         return;
48322     
48323         this.innerList.update(this.loadingText ?
48324                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48325         //this.restrictHeight();
48326         this.selectedIndex = -1;
48327     },
48328
48329     // private
48330     onLoad : function(){
48331
48332     
48333         var dom = this.el.dom;
48334         dom.innerHTML = '';
48335          var od = dom.ownerDocument;
48336          
48337         if (this.emptyText) {
48338             var op = od.createElement('option');
48339             op.setAttribute('value', '');
48340             op.innerHTML = String.format('{0}', this.emptyText);
48341             dom.appendChild(op);
48342         }
48343         if(this.store.getCount() > 0){
48344            
48345             var vf = this.valueField;
48346             var df = this.displayField;
48347             this.store.data.each(function(r) {
48348                 // which colmsn to use... testing - cdoe / title..
48349                 var op = od.createElement('option');
48350                 op.setAttribute('value', r.data[vf]);
48351                 op.innerHTML = String.format('{0}', r.data[df]);
48352                 dom.appendChild(op);
48353             });
48354             if (typeof(this.defaultValue != 'undefined')) {
48355                 this.setValue(this.defaultValue);
48356             }
48357             
48358              
48359         }else{
48360             //this.onEmptyResults();
48361         }
48362         //this.el.focus();
48363     },
48364     // private
48365     onLoadException : function()
48366     {
48367         dom.innerHTML = '';
48368             
48369         Roo.log("Select on load exception");
48370         return;
48371     
48372         this.collapse();
48373         Roo.log(this.store.reader.jsonData);
48374         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48375             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48376         }
48377         
48378         
48379     },
48380     // private
48381     onTypeAhead : function(){
48382          
48383     },
48384
48385     // private
48386     onSelect : function(record, index){
48387         Roo.log('on select?');
48388         return;
48389         if(this.fireEvent('beforeselect', this, record, index) !== false){
48390             this.setFromData(index > -1 ? record.data : false);
48391             this.collapse();
48392             this.fireEvent('select', this, record, index);
48393         }
48394     },
48395
48396     /**
48397      * Returns the currently selected field value or empty string if no value is set.
48398      * @return {String} value The selected value
48399      */
48400     getValue : function(){
48401         var dom = this.el.dom;
48402         this.value = dom.options[dom.selectedIndex].value;
48403         return this.value;
48404         
48405     },
48406
48407     /**
48408      * Clears any text/value currently set in the field
48409      */
48410     clearValue : function(){
48411         this.value = '';
48412         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48413         
48414     },
48415
48416     /**
48417      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48418      * will be displayed in the field.  If the value does not match the data value of an existing item,
48419      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48420      * Otherwise the field will be blank (although the value will still be set).
48421      * @param {String} value The value to match
48422      */
48423     setValue : function(v){
48424         var d = this.el.dom;
48425         for (var i =0; i < d.options.length;i++) {
48426             if (v == d.options[i].value) {
48427                 d.selectedIndex = i;
48428                 this.value = v;
48429                 return;
48430             }
48431         }
48432         this.clearValue();
48433     },
48434     /**
48435      * @property {Object} the last set data for the element
48436      */
48437     
48438     lastData : false,
48439     /**
48440      * Sets the value of the field based on a object which is related to the record format for the store.
48441      * @param {Object} value the value to set as. or false on reset?
48442      */
48443     setFromData : function(o){
48444         Roo.log('setfrom data?');
48445          
48446         
48447         
48448     },
48449     // private
48450     reset : function(){
48451         this.clearValue();
48452     },
48453     // private
48454     findRecord : function(prop, value){
48455         
48456         return false;
48457     
48458         var record;
48459         if(this.store.getCount() > 0){
48460             this.store.each(function(r){
48461                 if(r.data[prop] == value){
48462                     record = r;
48463                     return false;
48464                 }
48465                 return true;
48466             });
48467         }
48468         return record;
48469     },
48470     
48471     getName: function()
48472     {
48473         // returns hidden if it's set..
48474         if (!this.rendered) {return ''};
48475         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48476         
48477     },
48478      
48479
48480     
48481
48482     // private
48483     onEmptyResults : function(){
48484         Roo.log('empty results');
48485         //this.collapse();
48486     },
48487
48488     /**
48489      * Returns true if the dropdown list is expanded, else false.
48490      */
48491     isExpanded : function(){
48492         return false;
48493     },
48494
48495     /**
48496      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48497      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48498      * @param {String} value The data value of the item to select
48499      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48500      * selected item if it is not currently in view (defaults to true)
48501      * @return {Boolean} True if the value matched an item in the list, else false
48502      */
48503     selectByValue : function(v, scrollIntoView){
48504         Roo.log('select By Value');
48505         return false;
48506     
48507         if(v !== undefined && v !== null){
48508             var r = this.findRecord(this.valueField || this.displayField, v);
48509             if(r){
48510                 this.select(this.store.indexOf(r), scrollIntoView);
48511                 return true;
48512             }
48513         }
48514         return false;
48515     },
48516
48517     /**
48518      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48519      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48520      * @param {Number} index The zero-based index of the list item to select
48521      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48522      * selected item if it is not currently in view (defaults to true)
48523      */
48524     select : function(index, scrollIntoView){
48525         Roo.log('select ');
48526         return  ;
48527         
48528         this.selectedIndex = index;
48529         this.view.select(index);
48530         if(scrollIntoView !== false){
48531             var el = this.view.getNode(index);
48532             if(el){
48533                 this.innerList.scrollChildIntoView(el, false);
48534             }
48535         }
48536     },
48537
48538       
48539
48540     // private
48541     validateBlur : function(){
48542         
48543         return;
48544         
48545     },
48546
48547     // private
48548     initQuery : function(){
48549         this.doQuery(this.getRawValue());
48550     },
48551
48552     // private
48553     doForce : function(){
48554         if(this.el.dom.value.length > 0){
48555             this.el.dom.value =
48556                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48557              
48558         }
48559     },
48560
48561     /**
48562      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48563      * query allowing the query action to be canceled if needed.
48564      * @param {String} query The SQL query to execute
48565      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48566      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48567      * saved in the current store (defaults to false)
48568      */
48569     doQuery : function(q, forceAll){
48570         
48571         Roo.log('doQuery?');
48572         if(q === undefined || q === null){
48573             q = '';
48574         }
48575         var qe = {
48576             query: q,
48577             forceAll: forceAll,
48578             combo: this,
48579             cancel:false
48580         };
48581         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48582             return false;
48583         }
48584         q = qe.query;
48585         forceAll = qe.forceAll;
48586         if(forceAll === true || (q.length >= this.minChars)){
48587             if(this.lastQuery != q || this.alwaysQuery){
48588                 this.lastQuery = q;
48589                 if(this.mode == 'local'){
48590                     this.selectedIndex = -1;
48591                     if(forceAll){
48592                         this.store.clearFilter();
48593                     }else{
48594                         this.store.filter(this.displayField, q);
48595                     }
48596                     this.onLoad();
48597                 }else{
48598                     this.store.baseParams[this.queryParam] = q;
48599                     this.store.load({
48600                         params: this.getParams(q)
48601                     });
48602                     this.expand();
48603                 }
48604             }else{
48605                 this.selectedIndex = -1;
48606                 this.onLoad();   
48607             }
48608         }
48609     },
48610
48611     // private
48612     getParams : function(q){
48613         var p = {};
48614         //p[this.queryParam] = q;
48615         if(this.pageSize){
48616             p.start = 0;
48617             p.limit = this.pageSize;
48618         }
48619         return p;
48620     },
48621
48622     /**
48623      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48624      */
48625     collapse : function(){
48626         
48627     },
48628
48629     // private
48630     collapseIf : function(e){
48631         
48632     },
48633
48634     /**
48635      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48636      */
48637     expand : function(){
48638         
48639     } ,
48640
48641     // private
48642      
48643
48644     /** 
48645     * @cfg {Boolean} grow 
48646     * @hide 
48647     */
48648     /** 
48649     * @cfg {Number} growMin 
48650     * @hide 
48651     */
48652     /** 
48653     * @cfg {Number} growMax 
48654     * @hide 
48655     */
48656     /**
48657      * @hide
48658      * @method autoSize
48659      */
48660     
48661     setWidth : function()
48662     {
48663         
48664     },
48665     getResizeEl : function(){
48666         return this.el;
48667     }
48668 });//<script type="text/javasscript">
48669  
48670
48671 /**
48672  * @class Roo.DDView
48673  * A DnD enabled version of Roo.View.
48674  * @param {Element/String} container The Element in which to create the View.
48675  * @param {String} tpl The template string used to create the markup for each element of the View
48676  * @param {Object} config The configuration properties. These include all the config options of
48677  * {@link Roo.View} plus some specific to this class.<br>
48678  * <p>
48679  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48680  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48681  * <p>
48682  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48683 .x-view-drag-insert-above {
48684         border-top:1px dotted #3366cc;
48685 }
48686 .x-view-drag-insert-below {
48687         border-bottom:1px dotted #3366cc;
48688 }
48689 </code></pre>
48690  * 
48691  */
48692  
48693 Roo.DDView = function(container, tpl, config) {
48694     Roo.DDView.superclass.constructor.apply(this, arguments);
48695     this.getEl().setStyle("outline", "0px none");
48696     this.getEl().unselectable();
48697     if (this.dragGroup) {
48698                 this.setDraggable(this.dragGroup.split(","));
48699     }
48700     if (this.dropGroup) {
48701                 this.setDroppable(this.dropGroup.split(","));
48702     }
48703     if (this.deletable) {
48704         this.setDeletable();
48705     }
48706     this.isDirtyFlag = false;
48707         this.addEvents({
48708                 "drop" : true
48709         });
48710 };
48711
48712 Roo.extend(Roo.DDView, Roo.View, {
48713 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48714 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48715 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48716 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48717
48718         isFormField: true,
48719
48720         reset: Roo.emptyFn,
48721         
48722         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48723
48724         validate: function() {
48725                 return true;
48726         },
48727         
48728         destroy: function() {
48729                 this.purgeListeners();
48730                 this.getEl.removeAllListeners();
48731                 this.getEl().remove();
48732                 if (this.dragZone) {
48733                         if (this.dragZone.destroy) {
48734                                 this.dragZone.destroy();
48735                         }
48736                 }
48737                 if (this.dropZone) {
48738                         if (this.dropZone.destroy) {
48739                                 this.dropZone.destroy();
48740                         }
48741                 }
48742         },
48743
48744 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48745         getName: function() {
48746                 return this.name;
48747         },
48748
48749 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48750         setValue: function(v) {
48751                 if (!this.store) {
48752                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48753                 }
48754                 var data = {};
48755                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48756                 this.store.proxy = new Roo.data.MemoryProxy(data);
48757                 this.store.load();
48758         },
48759
48760 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48761         getValue: function() {
48762                 var result = '(';
48763                 this.store.each(function(rec) {
48764                         result += rec.id + ',';
48765                 });
48766                 return result.substr(0, result.length - 1) + ')';
48767         },
48768         
48769         getIds: function() {
48770                 var i = 0, result = new Array(this.store.getCount());
48771                 this.store.each(function(rec) {
48772                         result[i++] = rec.id;
48773                 });
48774                 return result;
48775         },
48776         
48777         isDirty: function() {
48778                 return this.isDirtyFlag;
48779         },
48780
48781 /**
48782  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48783  *      whole Element becomes the target, and this causes the drop gesture to append.
48784  */
48785     getTargetFromEvent : function(e) {
48786                 var target = e.getTarget();
48787                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48788                 target = target.parentNode;
48789                 }
48790                 if (!target) {
48791                         target = this.el.dom.lastChild || this.el.dom;
48792                 }
48793                 return target;
48794     },
48795
48796 /**
48797  *      Create the drag data which consists of an object which has the property "ddel" as
48798  *      the drag proxy element. 
48799  */
48800     getDragData : function(e) {
48801         var target = this.findItemFromChild(e.getTarget());
48802                 if(target) {
48803                         this.handleSelection(e);
48804                         var selNodes = this.getSelectedNodes();
48805             var dragData = {
48806                 source: this,
48807                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48808                 nodes: selNodes,
48809                 records: []
48810                         };
48811                         var selectedIndices = this.getSelectedIndexes();
48812                         for (var i = 0; i < selectedIndices.length; i++) {
48813                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48814                         }
48815                         if (selNodes.length == 1) {
48816                                 dragData.ddel = target.cloneNode(true); // the div element
48817                         } else {
48818                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48819                                 div.className = 'multi-proxy';
48820                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48821                                         div.appendChild(selNodes[i].cloneNode(true));
48822                                 }
48823                                 dragData.ddel = div;
48824                         }
48825             //console.log(dragData)
48826             //console.log(dragData.ddel.innerHTML)
48827                         return dragData;
48828                 }
48829         //console.log('nodragData')
48830                 return false;
48831     },
48832     
48833 /**     Specify to which ddGroup items in this DDView may be dragged. */
48834     setDraggable: function(ddGroup) {
48835         if (ddGroup instanceof Array) {
48836                 Roo.each(ddGroup, this.setDraggable, this);
48837                 return;
48838         }
48839         if (this.dragZone) {
48840                 this.dragZone.addToGroup(ddGroup);
48841         } else {
48842                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48843                                 containerScroll: true,
48844                                 ddGroup: ddGroup 
48845
48846                         });
48847 //                      Draggability implies selection. DragZone's mousedown selects the element.
48848                         if (!this.multiSelect) { this.singleSelect = true; }
48849
48850 //                      Wire the DragZone's handlers up to methods in *this*
48851                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48852                 }
48853     },
48854
48855 /**     Specify from which ddGroup this DDView accepts drops. */
48856     setDroppable: function(ddGroup) {
48857         if (ddGroup instanceof Array) {
48858                 Roo.each(ddGroup, this.setDroppable, this);
48859                 return;
48860         }
48861         if (this.dropZone) {
48862                 this.dropZone.addToGroup(ddGroup);
48863         } else {
48864                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48865                                 containerScroll: true,
48866                                 ddGroup: ddGroup
48867                         });
48868
48869 //                      Wire the DropZone's handlers up to methods in *this*
48870                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48871                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48872                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48873                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48874                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48875                 }
48876     },
48877
48878 /**     Decide whether to drop above or below a View node. */
48879     getDropPoint : function(e, n, dd){
48880         if (n == this.el.dom) { return "above"; }
48881                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48882                 var c = t + (b - t) / 2;
48883                 var y = Roo.lib.Event.getPageY(e);
48884                 if(y <= c) {
48885                         return "above";
48886                 }else{
48887                         return "below";
48888                 }
48889     },
48890
48891     onNodeEnter : function(n, dd, e, data){
48892                 return false;
48893     },
48894     
48895     onNodeOver : function(n, dd, e, data){
48896                 var pt = this.getDropPoint(e, n, dd);
48897                 // set the insert point style on the target node
48898                 var dragElClass = this.dropNotAllowed;
48899                 if (pt) {
48900                         var targetElClass;
48901                         if (pt == "above"){
48902                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48903                                 targetElClass = "x-view-drag-insert-above";
48904                         } else {
48905                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48906                                 targetElClass = "x-view-drag-insert-below";
48907                         }
48908                         if (this.lastInsertClass != targetElClass){
48909                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48910                                 this.lastInsertClass = targetElClass;
48911                         }
48912                 }
48913                 return dragElClass;
48914         },
48915
48916     onNodeOut : function(n, dd, e, data){
48917                 this.removeDropIndicators(n);
48918     },
48919
48920     onNodeDrop : function(n, dd, e, data){
48921         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48922                 return false;
48923         }
48924         var pt = this.getDropPoint(e, n, dd);
48925                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48926                 if (pt == "below") { insertAt++; }
48927                 for (var i = 0; i < data.records.length; i++) {
48928                         var r = data.records[i];
48929                         var dup = this.store.getById(r.id);
48930                         if (dup && (dd != this.dragZone)) {
48931                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48932                         } else {
48933                                 if (data.copy) {
48934                                         this.store.insert(insertAt++, r.copy());
48935                                 } else {
48936                                         data.source.isDirtyFlag = true;
48937                                         r.store.remove(r);
48938                                         this.store.insert(insertAt++, r);
48939                                 }
48940                                 this.isDirtyFlag = true;
48941                         }
48942                 }
48943                 this.dragZone.cachedTarget = null;
48944                 return true;
48945     },
48946
48947     removeDropIndicators : function(n){
48948                 if(n){
48949                         Roo.fly(n).removeClass([
48950                                 "x-view-drag-insert-above",
48951                                 "x-view-drag-insert-below"]);
48952                         this.lastInsertClass = "_noclass";
48953                 }
48954     },
48955
48956 /**
48957  *      Utility method. Add a delete option to the DDView's context menu.
48958  *      @param {String} imageUrl The URL of the "delete" icon image.
48959  */
48960         setDeletable: function(imageUrl) {
48961                 if (!this.singleSelect && !this.multiSelect) {
48962                         this.singleSelect = true;
48963                 }
48964                 var c = this.getContextMenu();
48965                 this.contextMenu.on("itemclick", function(item) {
48966                         switch (item.id) {
48967                                 case "delete":
48968                                         this.remove(this.getSelectedIndexes());
48969                                         break;
48970                         }
48971                 }, this);
48972                 this.contextMenu.add({
48973                         icon: imageUrl,
48974                         id: "delete",
48975                         text: 'Delete'
48976                 });
48977         },
48978         
48979 /**     Return the context menu for this DDView. */
48980         getContextMenu: function() {
48981                 if (!this.contextMenu) {
48982 //                      Create the View's context menu
48983                         this.contextMenu = new Roo.menu.Menu({
48984                                 id: this.id + "-contextmenu"
48985                         });
48986                         this.el.on("contextmenu", this.showContextMenu, this);
48987                 }
48988                 return this.contextMenu;
48989         },
48990         
48991         disableContextMenu: function() {
48992                 if (this.contextMenu) {
48993                         this.el.un("contextmenu", this.showContextMenu, this);
48994                 }
48995         },
48996
48997         showContextMenu: function(e, item) {
48998         item = this.findItemFromChild(e.getTarget());
48999                 if (item) {
49000                         e.stopEvent();
49001                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49002                         this.contextMenu.showAt(e.getXY());
49003             }
49004     },
49005
49006 /**
49007  *      Remove {@link Roo.data.Record}s at the specified indices.
49008  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49009  */
49010     remove: function(selectedIndices) {
49011                 selectedIndices = [].concat(selectedIndices);
49012                 for (var i = 0; i < selectedIndices.length; i++) {
49013                         var rec = this.store.getAt(selectedIndices[i]);
49014                         this.store.remove(rec);
49015                 }
49016     },
49017
49018 /**
49019  *      Double click fires the event, but also, if this is draggable, and there is only one other
49020  *      related DropZone, it transfers the selected node.
49021  */
49022     onDblClick : function(e){
49023         var item = this.findItemFromChild(e.getTarget());
49024         if(item){
49025             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49026                 return false;
49027             }
49028             if (this.dragGroup) {
49029                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49030                     while (targets.indexOf(this.dropZone) > -1) {
49031                             targets.remove(this.dropZone);
49032                                 }
49033                     if (targets.length == 1) {
49034                                         this.dragZone.cachedTarget = null;
49035                         var el = Roo.get(targets[0].getEl());
49036                         var box = el.getBox(true);
49037                         targets[0].onNodeDrop(el.dom, {
49038                                 target: el.dom,
49039                                 xy: [box.x, box.y + box.height - 1]
49040                         }, null, this.getDragData(e));
49041                     }
49042                 }
49043         }
49044     },
49045     
49046     handleSelection: function(e) {
49047                 this.dragZone.cachedTarget = null;
49048         var item = this.findItemFromChild(e.getTarget());
49049         if (!item) {
49050                 this.clearSelections(true);
49051                 return;
49052         }
49053                 if (item && (this.multiSelect || this.singleSelect)){
49054                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49055                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49056                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49057                                 this.unselect(item);
49058                         } else {
49059                                 this.select(item, this.multiSelect && e.ctrlKey);
49060                                 this.lastSelection = item;
49061                         }
49062                 }
49063     },
49064
49065     onItemClick : function(item, index, e){
49066                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49067                         return false;
49068                 }
49069                 return true;
49070     },
49071
49072     unselect : function(nodeInfo, suppressEvent){
49073                 var node = this.getNode(nodeInfo);
49074                 if(node && this.isSelected(node)){
49075                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49076                                 Roo.fly(node).removeClass(this.selectedClass);
49077                                 this.selections.remove(node);
49078                                 if(!suppressEvent){
49079                                         this.fireEvent("selectionchange", this, this.selections);
49080                                 }
49081                         }
49082                 }
49083     }
49084 });
49085 /*
49086  * Based on:
49087  * Ext JS Library 1.1.1
49088  * Copyright(c) 2006-2007, Ext JS, LLC.
49089  *
49090  * Originally Released Under LGPL - original licence link has changed is not relivant.
49091  *
49092  * Fork - LGPL
49093  * <script type="text/javascript">
49094  */
49095  
49096 /**
49097  * @class Roo.LayoutManager
49098  * @extends Roo.util.Observable
49099  * Base class for layout managers.
49100  */
49101 Roo.LayoutManager = function(container, config){
49102     Roo.LayoutManager.superclass.constructor.call(this);
49103     this.el = Roo.get(container);
49104     // ie scrollbar fix
49105     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49106         document.body.scroll = "no";
49107     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49108         this.el.position('relative');
49109     }
49110     this.id = this.el.id;
49111     this.el.addClass("x-layout-container");
49112     /** false to disable window resize monitoring @type Boolean */
49113     this.monitorWindowResize = true;
49114     this.regions = {};
49115     this.addEvents({
49116         /**
49117          * @event layout
49118          * Fires when a layout is performed. 
49119          * @param {Roo.LayoutManager} this
49120          */
49121         "layout" : true,
49122         /**
49123          * @event regionresized
49124          * Fires when the user resizes a region. 
49125          * @param {Roo.LayoutRegion} region The resized region
49126          * @param {Number} newSize The new size (width for east/west, height for north/south)
49127          */
49128         "regionresized" : true,
49129         /**
49130          * @event regioncollapsed
49131          * Fires when a region is collapsed. 
49132          * @param {Roo.LayoutRegion} region The collapsed region
49133          */
49134         "regioncollapsed" : true,
49135         /**
49136          * @event regionexpanded
49137          * Fires when a region is expanded.  
49138          * @param {Roo.LayoutRegion} region The expanded region
49139          */
49140         "regionexpanded" : true
49141     });
49142     this.updating = false;
49143     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49144 };
49145
49146 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49147     /**
49148      * Returns true if this layout is currently being updated
49149      * @return {Boolean}
49150      */
49151     isUpdating : function(){
49152         return this.updating; 
49153     },
49154     
49155     /**
49156      * Suspend the LayoutManager from doing auto-layouts while
49157      * making multiple add or remove calls
49158      */
49159     beginUpdate : function(){
49160         this.updating = true;    
49161     },
49162     
49163     /**
49164      * Restore auto-layouts and optionally disable the manager from performing a layout
49165      * @param {Boolean} noLayout true to disable a layout update 
49166      */
49167     endUpdate : function(noLayout){
49168         this.updating = false;
49169         if(!noLayout){
49170             this.layout();
49171         }    
49172     },
49173     
49174     layout: function(){
49175         
49176     },
49177     
49178     onRegionResized : function(region, newSize){
49179         this.fireEvent("regionresized", region, newSize);
49180         this.layout();
49181     },
49182     
49183     onRegionCollapsed : function(region){
49184         this.fireEvent("regioncollapsed", region);
49185     },
49186     
49187     onRegionExpanded : function(region){
49188         this.fireEvent("regionexpanded", region);
49189     },
49190         
49191     /**
49192      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49193      * performs box-model adjustments.
49194      * @return {Object} The size as an object {width: (the width), height: (the height)}
49195      */
49196     getViewSize : function(){
49197         var size;
49198         if(this.el.dom != document.body){
49199             size = this.el.getSize();
49200         }else{
49201             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49202         }
49203         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49204         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49205         return size;
49206     },
49207     
49208     /**
49209      * Returns the Element this layout is bound to.
49210      * @return {Roo.Element}
49211      */
49212     getEl : function(){
49213         return this.el;
49214     },
49215     
49216     /**
49217      * Returns the specified region.
49218      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49219      * @return {Roo.LayoutRegion}
49220      */
49221     getRegion : function(target){
49222         return this.regions[target.toLowerCase()];
49223     },
49224     
49225     onWindowResize : function(){
49226         if(this.monitorWindowResize){
49227             this.layout();
49228         }
49229     }
49230 });/*
49231  * Based on:
49232  * Ext JS Library 1.1.1
49233  * Copyright(c) 2006-2007, Ext JS, LLC.
49234  *
49235  * Originally Released Under LGPL - original licence link has changed is not relivant.
49236  *
49237  * Fork - LGPL
49238  * <script type="text/javascript">
49239  */
49240 /**
49241  * @class Roo.BorderLayout
49242  * @extends Roo.LayoutManager
49243  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49244  * please see: <br><br>
49245  * <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>
49246  * <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>
49247  * Example:
49248  <pre><code>
49249  var layout = new Roo.BorderLayout(document.body, {
49250     north: {
49251         initialSize: 25,
49252         titlebar: false
49253     },
49254     west: {
49255         split:true,
49256         initialSize: 200,
49257         minSize: 175,
49258         maxSize: 400,
49259         titlebar: true,
49260         collapsible: true
49261     },
49262     east: {
49263         split:true,
49264         initialSize: 202,
49265         minSize: 175,
49266         maxSize: 400,
49267         titlebar: true,
49268         collapsible: true
49269     },
49270     south: {
49271         split:true,
49272         initialSize: 100,
49273         minSize: 100,
49274         maxSize: 200,
49275         titlebar: true,
49276         collapsible: true
49277     },
49278     center: {
49279         titlebar: true,
49280         autoScroll:true,
49281         resizeTabs: true,
49282         minTabWidth: 50,
49283         preferredTabWidth: 150
49284     }
49285 });
49286
49287 // shorthand
49288 var CP = Roo.ContentPanel;
49289
49290 layout.beginUpdate();
49291 layout.add("north", new CP("north", "North"));
49292 layout.add("south", new CP("south", {title: "South", closable: true}));
49293 layout.add("west", new CP("west", {title: "West"}));
49294 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49295 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49296 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49297 layout.getRegion("center").showPanel("center1");
49298 layout.endUpdate();
49299 </code></pre>
49300
49301 <b>The container the layout is rendered into can be either the body element or any other element.
49302 If it is not the body element, the container needs to either be an absolute positioned element,
49303 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49304 the container size if it is not the body element.</b>
49305
49306 * @constructor
49307 * Create a new BorderLayout
49308 * @param {String/HTMLElement/Element} container The container this layout is bound to
49309 * @param {Object} config Configuration options
49310  */
49311 Roo.BorderLayout = function(container, config){
49312     config = config || {};
49313     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49314     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49315     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49316         var target = this.factory.validRegions[i];
49317         if(config[target]){
49318             this.addRegion(target, config[target]);
49319         }
49320     }
49321 };
49322
49323 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49324     /**
49325      * Creates and adds a new region if it doesn't already exist.
49326      * @param {String} target The target region key (north, south, east, west or center).
49327      * @param {Object} config The regions config object
49328      * @return {BorderLayoutRegion} The new region
49329      */
49330     addRegion : function(target, config){
49331         if(!this.regions[target]){
49332             var r = this.factory.create(target, this, config);
49333             this.bindRegion(target, r);
49334         }
49335         return this.regions[target];
49336     },
49337
49338     // private (kinda)
49339     bindRegion : function(name, r){
49340         this.regions[name] = r;
49341         r.on("visibilitychange", this.layout, this);
49342         r.on("paneladded", this.layout, this);
49343         r.on("panelremoved", this.layout, this);
49344         r.on("invalidated", this.layout, this);
49345         r.on("resized", this.onRegionResized, this);
49346         r.on("collapsed", this.onRegionCollapsed, this);
49347         r.on("expanded", this.onRegionExpanded, this);
49348     },
49349
49350     /**
49351      * Performs a layout update.
49352      */
49353     layout : function(){
49354         if(this.updating) return;
49355         var size = this.getViewSize();
49356         var w = size.width;
49357         var h = size.height;
49358         var centerW = w;
49359         var centerH = h;
49360         var centerY = 0;
49361         var centerX = 0;
49362         //var x = 0, y = 0;
49363
49364         var rs = this.regions;
49365         var north = rs["north"];
49366         var south = rs["south"]; 
49367         var west = rs["west"];
49368         var east = rs["east"];
49369         var center = rs["center"];
49370         //if(this.hideOnLayout){ // not supported anymore
49371             //c.el.setStyle("display", "none");
49372         //}
49373         if(north && north.isVisible()){
49374             var b = north.getBox();
49375             var m = north.getMargins();
49376             b.width = w - (m.left+m.right);
49377             b.x = m.left;
49378             b.y = m.top;
49379             centerY = b.height + b.y + m.bottom;
49380             centerH -= centerY;
49381             north.updateBox(this.safeBox(b));
49382         }
49383         if(south && south.isVisible()){
49384             var b = south.getBox();
49385             var m = south.getMargins();
49386             b.width = w - (m.left+m.right);
49387             b.x = m.left;
49388             var totalHeight = (b.height + m.top + m.bottom);
49389             b.y = h - totalHeight + m.top;
49390             centerH -= totalHeight;
49391             south.updateBox(this.safeBox(b));
49392         }
49393         if(west && west.isVisible()){
49394             var b = west.getBox();
49395             var m = west.getMargins();
49396             b.height = centerH - (m.top+m.bottom);
49397             b.x = m.left;
49398             b.y = centerY + m.top;
49399             var totalWidth = (b.width + m.left + m.right);
49400             centerX += totalWidth;
49401             centerW -= totalWidth;
49402             west.updateBox(this.safeBox(b));
49403         }
49404         if(east && east.isVisible()){
49405             var b = east.getBox();
49406             var m = east.getMargins();
49407             b.height = centerH - (m.top+m.bottom);
49408             var totalWidth = (b.width + m.left + m.right);
49409             b.x = w - totalWidth + m.left;
49410             b.y = centerY + m.top;
49411             centerW -= totalWidth;
49412             east.updateBox(this.safeBox(b));
49413         }
49414         if(center){
49415             var m = center.getMargins();
49416             var centerBox = {
49417                 x: centerX + m.left,
49418                 y: centerY + m.top,
49419                 width: centerW - (m.left+m.right),
49420                 height: centerH - (m.top+m.bottom)
49421             };
49422             //if(this.hideOnLayout){
49423                 //center.el.setStyle("display", "block");
49424             //}
49425             center.updateBox(this.safeBox(centerBox));
49426         }
49427         this.el.repaint();
49428         this.fireEvent("layout", this);
49429     },
49430
49431     // private
49432     safeBox : function(box){
49433         box.width = Math.max(0, box.width);
49434         box.height = Math.max(0, box.height);
49435         return box;
49436     },
49437
49438     /**
49439      * Adds a ContentPanel (or subclass) to this layout.
49440      * @param {String} target The target region key (north, south, east, west or center).
49441      * @param {Roo.ContentPanel} panel The panel to add
49442      * @return {Roo.ContentPanel} The added panel
49443      */
49444     add : function(target, panel){
49445          
49446         target = target.toLowerCase();
49447         return this.regions[target].add(panel);
49448     },
49449
49450     /**
49451      * Remove a ContentPanel (or subclass) to this layout.
49452      * @param {String} target The target region key (north, south, east, west or center).
49453      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49454      * @return {Roo.ContentPanel} The removed panel
49455      */
49456     remove : function(target, panel){
49457         target = target.toLowerCase();
49458         return this.regions[target].remove(panel);
49459     },
49460
49461     /**
49462      * Searches all regions for a panel with the specified id
49463      * @param {String} panelId
49464      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49465      */
49466     findPanel : function(panelId){
49467         var rs = this.regions;
49468         for(var target in rs){
49469             if(typeof rs[target] != "function"){
49470                 var p = rs[target].getPanel(panelId);
49471                 if(p){
49472                     return p;
49473                 }
49474             }
49475         }
49476         return null;
49477     },
49478
49479     /**
49480      * Searches all regions for a panel with the specified id and activates (shows) it.
49481      * @param {String/ContentPanel} panelId The panels id or the panel itself
49482      * @return {Roo.ContentPanel} The shown panel or null
49483      */
49484     showPanel : function(panelId) {
49485       var rs = this.regions;
49486       for(var target in rs){
49487          var r = rs[target];
49488          if(typeof r != "function"){
49489             if(r.hasPanel(panelId)){
49490                return r.showPanel(panelId);
49491             }
49492          }
49493       }
49494       return null;
49495    },
49496
49497    /**
49498      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49499      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49500      */
49501     restoreState : function(provider){
49502         if(!provider){
49503             provider = Roo.state.Manager;
49504         }
49505         var sm = new Roo.LayoutStateManager();
49506         sm.init(this, provider);
49507     },
49508
49509     /**
49510      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49511      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49512      * a valid ContentPanel config object.  Example:
49513      * <pre><code>
49514 // Create the main layout
49515 var layout = new Roo.BorderLayout('main-ct', {
49516     west: {
49517         split:true,
49518         minSize: 175,
49519         titlebar: true
49520     },
49521     center: {
49522         title:'Components'
49523     }
49524 }, 'main-ct');
49525
49526 // Create and add multiple ContentPanels at once via configs
49527 layout.batchAdd({
49528    west: {
49529        id: 'source-files',
49530        autoCreate:true,
49531        title:'Ext Source Files',
49532        autoScroll:true,
49533        fitToFrame:true
49534    },
49535    center : {
49536        el: cview,
49537        autoScroll:true,
49538        fitToFrame:true,
49539        toolbar: tb,
49540        resizeEl:'cbody'
49541    }
49542 });
49543 </code></pre>
49544      * @param {Object} regions An object containing ContentPanel configs by region name
49545      */
49546     batchAdd : function(regions){
49547         this.beginUpdate();
49548         for(var rname in regions){
49549             var lr = this.regions[rname];
49550             if(lr){
49551                 this.addTypedPanels(lr, regions[rname]);
49552             }
49553         }
49554         this.endUpdate();
49555     },
49556
49557     // private
49558     addTypedPanels : function(lr, ps){
49559         if(typeof ps == 'string'){
49560             lr.add(new Roo.ContentPanel(ps));
49561         }
49562         else if(ps instanceof Array){
49563             for(var i =0, len = ps.length; i < len; i++){
49564                 this.addTypedPanels(lr, ps[i]);
49565             }
49566         }
49567         else if(!ps.events){ // raw config?
49568             var el = ps.el;
49569             delete ps.el; // prevent conflict
49570             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49571         }
49572         else {  // panel object assumed!
49573             lr.add(ps);
49574         }
49575     },
49576     /**
49577      * Adds a xtype elements to the layout.
49578      * <pre><code>
49579
49580 layout.addxtype({
49581        xtype : 'ContentPanel',
49582        region: 'west',
49583        items: [ .... ]
49584    }
49585 );
49586
49587 layout.addxtype({
49588         xtype : 'NestedLayoutPanel',
49589         region: 'west',
49590         layout: {
49591            center: { },
49592            west: { }   
49593         },
49594         items : [ ... list of content panels or nested layout panels.. ]
49595    }
49596 );
49597 </code></pre>
49598      * @param {Object} cfg Xtype definition of item to add.
49599      */
49600     addxtype : function(cfg)
49601     {
49602         // basically accepts a pannel...
49603         // can accept a layout region..!?!?
49604         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49605         
49606         if (!cfg.xtype.match(/Panel$/)) {
49607             return false;
49608         }
49609         var ret = false;
49610         
49611         if (typeof(cfg.region) == 'undefined') {
49612             Roo.log("Failed to add Panel, region was not set");
49613             Roo.log(cfg);
49614             return false;
49615         }
49616         var region = cfg.region;
49617         delete cfg.region;
49618         
49619           
49620         var xitems = [];
49621         if (cfg.items) {
49622             xitems = cfg.items;
49623             delete cfg.items;
49624         }
49625         var nb = false;
49626         
49627         switch(cfg.xtype) 
49628         {
49629             case 'ContentPanel':  // ContentPanel (el, cfg)
49630             case 'ScrollPanel':  // ContentPanel (el, cfg)
49631             case 'ViewPanel': 
49632                 if(cfg.autoCreate) {
49633                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49634                 } else {
49635                     var el = this.el.createChild();
49636                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49637                 }
49638                 
49639                 this.add(region, ret);
49640                 break;
49641             
49642             
49643             case 'TreePanel': // our new panel!
49644                 cfg.el = this.el.createChild();
49645                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49646                 this.add(region, ret);
49647                 break;
49648             
49649             case 'NestedLayoutPanel': 
49650                 // create a new Layout (which is  a Border Layout...
49651                 var el = this.el.createChild();
49652                 var clayout = cfg.layout;
49653                 delete cfg.layout;
49654                 clayout.items   = clayout.items  || [];
49655                 // replace this exitems with the clayout ones..
49656                 xitems = clayout.items;
49657                  
49658                 
49659                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49660                     cfg.background = false;
49661                 }
49662                 var layout = new Roo.BorderLayout(el, clayout);
49663                 
49664                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49665                 //console.log('adding nested layout panel '  + cfg.toSource());
49666                 this.add(region, ret);
49667                 nb = {}; /// find first...
49668                 break;
49669                 
49670             case 'GridPanel': 
49671             
49672                 // needs grid and region
49673                 
49674                 //var el = this.getRegion(region).el.createChild();
49675                 var el = this.el.createChild();
49676                 // create the grid first...
49677                 
49678                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49679                 delete cfg.grid;
49680                 if (region == 'center' && this.active ) {
49681                     cfg.background = false;
49682                 }
49683                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49684                 
49685                 this.add(region, ret);
49686                 if (cfg.background) {
49687                     ret.on('activate', function(gp) {
49688                         if (!gp.grid.rendered) {
49689                             gp.grid.render();
49690                         }
49691                     });
49692                 } else {
49693                     grid.render();
49694                 }
49695                 break;
49696            
49697            
49698            
49699                 
49700                 
49701                 
49702             default:
49703                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49704                     
49705                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49706                     this.add(region, ret);
49707                 } else {
49708                 
49709                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49710                     return null;
49711                 }
49712                 
49713              // GridPanel (grid, cfg)
49714             
49715         }
49716         this.beginUpdate();
49717         // add children..
49718         var region = '';
49719         var abn = {};
49720         Roo.each(xitems, function(i)  {
49721             region = nb && i.region ? i.region : false;
49722             
49723             var add = ret.addxtype(i);
49724            
49725             if (region) {
49726                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49727                 if (!i.background) {
49728                     abn[region] = nb[region] ;
49729                 }
49730             }
49731             
49732         });
49733         this.endUpdate();
49734
49735         // make the last non-background panel active..
49736         //if (nb) { Roo.log(abn); }
49737         if (nb) {
49738             
49739             for(var r in abn) {
49740                 region = this.getRegion(r);
49741                 if (region) {
49742                     // tried using nb[r], but it does not work..
49743                      
49744                     region.showPanel(abn[r]);
49745                    
49746                 }
49747             }
49748         }
49749         return ret;
49750         
49751     }
49752 });
49753
49754 /**
49755  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49756  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49757  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49758  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49759  * <pre><code>
49760 // shorthand
49761 var CP = Roo.ContentPanel;
49762
49763 var layout = Roo.BorderLayout.create({
49764     north: {
49765         initialSize: 25,
49766         titlebar: false,
49767         panels: [new CP("north", "North")]
49768     },
49769     west: {
49770         split:true,
49771         initialSize: 200,
49772         minSize: 175,
49773         maxSize: 400,
49774         titlebar: true,
49775         collapsible: true,
49776         panels: [new CP("west", {title: "West"})]
49777     },
49778     east: {
49779         split:true,
49780         initialSize: 202,
49781         minSize: 175,
49782         maxSize: 400,
49783         titlebar: true,
49784         collapsible: true,
49785         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49786     },
49787     south: {
49788         split:true,
49789         initialSize: 100,
49790         minSize: 100,
49791         maxSize: 200,
49792         titlebar: true,
49793         collapsible: true,
49794         panels: [new CP("south", {title: "South", closable: true})]
49795     },
49796     center: {
49797         titlebar: true,
49798         autoScroll:true,
49799         resizeTabs: true,
49800         minTabWidth: 50,
49801         preferredTabWidth: 150,
49802         panels: [
49803             new CP("center1", {title: "Close Me", closable: true}),
49804             new CP("center2", {title: "Center Panel", closable: false})
49805         ]
49806     }
49807 }, document.body);
49808
49809 layout.getRegion("center").showPanel("center1");
49810 </code></pre>
49811  * @param config
49812  * @param targetEl
49813  */
49814 Roo.BorderLayout.create = function(config, targetEl){
49815     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49816     layout.beginUpdate();
49817     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49818     for(var j = 0, jlen = regions.length; j < jlen; j++){
49819         var lr = regions[j];
49820         if(layout.regions[lr] && config[lr].panels){
49821             var r = layout.regions[lr];
49822             var ps = config[lr].panels;
49823             layout.addTypedPanels(r, ps);
49824         }
49825     }
49826     layout.endUpdate();
49827     return layout;
49828 };
49829
49830 // private
49831 Roo.BorderLayout.RegionFactory = {
49832     // private
49833     validRegions : ["north","south","east","west","center"],
49834
49835     // private
49836     create : function(target, mgr, config){
49837         target = target.toLowerCase();
49838         if(config.lightweight || config.basic){
49839             return new Roo.BasicLayoutRegion(mgr, config, target);
49840         }
49841         switch(target){
49842             case "north":
49843                 return new Roo.NorthLayoutRegion(mgr, config);
49844             case "south":
49845                 return new Roo.SouthLayoutRegion(mgr, config);
49846             case "east":
49847                 return new Roo.EastLayoutRegion(mgr, config);
49848             case "west":
49849                 return new Roo.WestLayoutRegion(mgr, config);
49850             case "center":
49851                 return new Roo.CenterLayoutRegion(mgr, config);
49852         }
49853         throw 'Layout region "'+target+'" not supported.';
49854     }
49855 };/*
49856  * Based on:
49857  * Ext JS Library 1.1.1
49858  * Copyright(c) 2006-2007, Ext JS, LLC.
49859  *
49860  * Originally Released Under LGPL - original licence link has changed is not relivant.
49861  *
49862  * Fork - LGPL
49863  * <script type="text/javascript">
49864  */
49865  
49866 /**
49867  * @class Roo.BasicLayoutRegion
49868  * @extends Roo.util.Observable
49869  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49870  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49871  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49872  */
49873 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49874     this.mgr = mgr;
49875     this.position  = pos;
49876     this.events = {
49877         /**
49878          * @scope Roo.BasicLayoutRegion
49879          */
49880         
49881         /**
49882          * @event beforeremove
49883          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49884          * @param {Roo.LayoutRegion} this
49885          * @param {Roo.ContentPanel} panel The panel
49886          * @param {Object} e The cancel event object
49887          */
49888         "beforeremove" : true,
49889         /**
49890          * @event invalidated
49891          * Fires when the layout for this region is changed.
49892          * @param {Roo.LayoutRegion} this
49893          */
49894         "invalidated" : true,
49895         /**
49896          * @event visibilitychange
49897          * Fires when this region is shown or hidden 
49898          * @param {Roo.LayoutRegion} this
49899          * @param {Boolean} visibility true or false
49900          */
49901         "visibilitychange" : true,
49902         /**
49903          * @event paneladded
49904          * Fires when a panel is added. 
49905          * @param {Roo.LayoutRegion} this
49906          * @param {Roo.ContentPanel} panel The panel
49907          */
49908         "paneladded" : true,
49909         /**
49910          * @event panelremoved
49911          * Fires when a panel is removed. 
49912          * @param {Roo.LayoutRegion} this
49913          * @param {Roo.ContentPanel} panel The panel
49914          */
49915         "panelremoved" : true,
49916         /**
49917          * @event collapsed
49918          * Fires when this region is collapsed.
49919          * @param {Roo.LayoutRegion} this
49920          */
49921         "collapsed" : true,
49922         /**
49923          * @event expanded
49924          * Fires when this region is expanded.
49925          * @param {Roo.LayoutRegion} this
49926          */
49927         "expanded" : true,
49928         /**
49929          * @event slideshow
49930          * Fires when this region is slid into view.
49931          * @param {Roo.LayoutRegion} this
49932          */
49933         "slideshow" : true,
49934         /**
49935          * @event slidehide
49936          * Fires when this region slides out of view. 
49937          * @param {Roo.LayoutRegion} this
49938          */
49939         "slidehide" : true,
49940         /**
49941          * @event panelactivated
49942          * Fires when a panel is activated. 
49943          * @param {Roo.LayoutRegion} this
49944          * @param {Roo.ContentPanel} panel The activated panel
49945          */
49946         "panelactivated" : true,
49947         /**
49948          * @event resized
49949          * Fires when the user resizes this region. 
49950          * @param {Roo.LayoutRegion} this
49951          * @param {Number} newSize The new size (width for east/west, height for north/south)
49952          */
49953         "resized" : true
49954     };
49955     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49956     this.panels = new Roo.util.MixedCollection();
49957     this.panels.getKey = this.getPanelId.createDelegate(this);
49958     this.box = null;
49959     this.activePanel = null;
49960     // ensure listeners are added...
49961     
49962     if (config.listeners || config.events) {
49963         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49964             listeners : config.listeners || {},
49965             events : config.events || {}
49966         });
49967     }
49968     
49969     if(skipConfig !== true){
49970         this.applyConfig(config);
49971     }
49972 };
49973
49974 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49975     getPanelId : function(p){
49976         return p.getId();
49977     },
49978     
49979     applyConfig : function(config){
49980         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49981         this.config = config;
49982         
49983     },
49984     
49985     /**
49986      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49987      * the width, for horizontal (north, south) the height.
49988      * @param {Number} newSize The new width or height
49989      */
49990     resizeTo : function(newSize){
49991         var el = this.el ? this.el :
49992                  (this.activePanel ? this.activePanel.getEl() : null);
49993         if(el){
49994             switch(this.position){
49995                 case "east":
49996                 case "west":
49997                     el.setWidth(newSize);
49998                     this.fireEvent("resized", this, newSize);
49999                 break;
50000                 case "north":
50001                 case "south":
50002                     el.setHeight(newSize);
50003                     this.fireEvent("resized", this, newSize);
50004                 break;                
50005             }
50006         }
50007     },
50008     
50009     getBox : function(){
50010         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50011     },
50012     
50013     getMargins : function(){
50014         return this.margins;
50015     },
50016     
50017     updateBox : function(box){
50018         this.box = box;
50019         var el = this.activePanel.getEl();
50020         el.dom.style.left = box.x + "px";
50021         el.dom.style.top = box.y + "px";
50022         this.activePanel.setSize(box.width, box.height);
50023     },
50024     
50025     /**
50026      * Returns the container element for this region.
50027      * @return {Roo.Element}
50028      */
50029     getEl : function(){
50030         return this.activePanel;
50031     },
50032     
50033     /**
50034      * Returns true if this region is currently visible.
50035      * @return {Boolean}
50036      */
50037     isVisible : function(){
50038         return this.activePanel ? true : false;
50039     },
50040     
50041     setActivePanel : function(panel){
50042         panel = this.getPanel(panel);
50043         if(this.activePanel && this.activePanel != panel){
50044             this.activePanel.setActiveState(false);
50045             this.activePanel.getEl().setLeftTop(-10000,-10000);
50046         }
50047         this.activePanel = panel;
50048         panel.setActiveState(true);
50049         if(this.box){
50050             panel.setSize(this.box.width, this.box.height);
50051         }
50052         this.fireEvent("panelactivated", this, panel);
50053         this.fireEvent("invalidated");
50054     },
50055     
50056     /**
50057      * Show the specified panel.
50058      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50059      * @return {Roo.ContentPanel} The shown panel or null
50060      */
50061     showPanel : function(panel){
50062         if(panel = this.getPanel(panel)){
50063             this.setActivePanel(panel);
50064         }
50065         return panel;
50066     },
50067     
50068     /**
50069      * Get the active panel for this region.
50070      * @return {Roo.ContentPanel} The active panel or null
50071      */
50072     getActivePanel : function(){
50073         return this.activePanel;
50074     },
50075     
50076     /**
50077      * Add the passed ContentPanel(s)
50078      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50079      * @return {Roo.ContentPanel} The panel added (if only one was added)
50080      */
50081     add : function(panel){
50082         if(arguments.length > 1){
50083             for(var i = 0, len = arguments.length; i < len; i++) {
50084                 this.add(arguments[i]);
50085             }
50086             return null;
50087         }
50088         if(this.hasPanel(panel)){
50089             this.showPanel(panel);
50090             return panel;
50091         }
50092         var el = panel.getEl();
50093         if(el.dom.parentNode != this.mgr.el.dom){
50094             this.mgr.el.dom.appendChild(el.dom);
50095         }
50096         if(panel.setRegion){
50097             panel.setRegion(this);
50098         }
50099         this.panels.add(panel);
50100         el.setStyle("position", "absolute");
50101         if(!panel.background){
50102             this.setActivePanel(panel);
50103             if(this.config.initialSize && this.panels.getCount()==1){
50104                 this.resizeTo(this.config.initialSize);
50105             }
50106         }
50107         this.fireEvent("paneladded", this, panel);
50108         return panel;
50109     },
50110     
50111     /**
50112      * Returns true if the panel is in this region.
50113      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50114      * @return {Boolean}
50115      */
50116     hasPanel : function(panel){
50117         if(typeof panel == "object"){ // must be panel obj
50118             panel = panel.getId();
50119         }
50120         return this.getPanel(panel) ? true : false;
50121     },
50122     
50123     /**
50124      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50125      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50126      * @param {Boolean} preservePanel Overrides the config preservePanel option
50127      * @return {Roo.ContentPanel} The panel that was removed
50128      */
50129     remove : function(panel, preservePanel){
50130         panel = this.getPanel(panel);
50131         if(!panel){
50132             return null;
50133         }
50134         var e = {};
50135         this.fireEvent("beforeremove", this, panel, e);
50136         if(e.cancel === true){
50137             return null;
50138         }
50139         var panelId = panel.getId();
50140         this.panels.removeKey(panelId);
50141         return panel;
50142     },
50143     
50144     /**
50145      * Returns the panel specified or null if it's not in this region.
50146      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50147      * @return {Roo.ContentPanel}
50148      */
50149     getPanel : function(id){
50150         if(typeof id == "object"){ // must be panel obj
50151             return id;
50152         }
50153         return this.panels.get(id);
50154     },
50155     
50156     /**
50157      * Returns this regions position (north/south/east/west/center).
50158      * @return {String} 
50159      */
50160     getPosition: function(){
50161         return this.position;    
50162     }
50163 });/*
50164  * Based on:
50165  * Ext JS Library 1.1.1
50166  * Copyright(c) 2006-2007, Ext JS, LLC.
50167  *
50168  * Originally Released Under LGPL - original licence link has changed is not relivant.
50169  *
50170  * Fork - LGPL
50171  * <script type="text/javascript">
50172  */
50173  
50174 /**
50175  * @class Roo.LayoutRegion
50176  * @extends Roo.BasicLayoutRegion
50177  * This class represents a region in a layout manager.
50178  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50179  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50180  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50181  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50182  * @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})
50183  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50184  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50185  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50186  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50187  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50188  * @cfg {String}    title           The title for the region (overrides panel titles)
50189  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50190  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50191  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50192  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50193  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50194  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50195  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50196  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50197  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50198  * @cfg {Boolean}   showPin         True to show a pin button
50199  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50200  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50201  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50202  * @cfg {Number}    width           For East/West panels
50203  * @cfg {Number}    height          For North/South panels
50204  * @cfg {Boolean}   split           To show the splitter
50205  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50206  */
50207 Roo.LayoutRegion = function(mgr, config, pos){
50208     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50209     var dh = Roo.DomHelper;
50210     /** This region's container element 
50211     * @type Roo.Element */
50212     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50213     /** This region's title element 
50214     * @type Roo.Element */
50215
50216     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50217         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50218         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50219     ]}, true);
50220     this.titleEl.enableDisplayMode();
50221     /** This region's title text element 
50222     * @type HTMLElement */
50223     this.titleTextEl = this.titleEl.dom.firstChild;
50224     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50225     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50226     this.closeBtn.enableDisplayMode();
50227     this.closeBtn.on("click", this.closeClicked, this);
50228     this.closeBtn.hide();
50229
50230     this.createBody(config);
50231     this.visible = true;
50232     this.collapsed = false;
50233
50234     if(config.hideWhenEmpty){
50235         this.hide();
50236         this.on("paneladded", this.validateVisibility, this);
50237         this.on("panelremoved", this.validateVisibility, this);
50238     }
50239     this.applyConfig(config);
50240 };
50241
50242 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50243
50244     createBody : function(){
50245         /** This region's body element 
50246         * @type Roo.Element */
50247         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50248     },
50249
50250     applyConfig : function(c){
50251         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50252             var dh = Roo.DomHelper;
50253             if(c.titlebar !== false){
50254                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50255                 this.collapseBtn.on("click", this.collapse, this);
50256                 this.collapseBtn.enableDisplayMode();
50257
50258                 if(c.showPin === true || this.showPin){
50259                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50260                     this.stickBtn.enableDisplayMode();
50261                     this.stickBtn.on("click", this.expand, this);
50262                     this.stickBtn.hide();
50263                 }
50264             }
50265             /** This region's collapsed element
50266             * @type Roo.Element */
50267             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50268                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50269             ]}, true);
50270             if(c.floatable !== false){
50271                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50272                this.collapsedEl.on("click", this.collapseClick, this);
50273             }
50274
50275             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50276                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50277                    id: "message", unselectable: "on", style:{"float":"left"}});
50278                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50279              }
50280             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50281             this.expandBtn.on("click", this.expand, this);
50282         }
50283         if(this.collapseBtn){
50284             this.collapseBtn.setVisible(c.collapsible == true);
50285         }
50286         this.cmargins = c.cmargins || this.cmargins ||
50287                          (this.position == "west" || this.position == "east" ?
50288                              {top: 0, left: 2, right:2, bottom: 0} :
50289                              {top: 2, left: 0, right:0, bottom: 2});
50290         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50291         this.bottomTabs = c.tabPosition != "top";
50292         this.autoScroll = c.autoScroll || false;
50293         if(this.autoScroll){
50294             this.bodyEl.setStyle("overflow", "auto");
50295         }else{
50296             this.bodyEl.setStyle("overflow", "hidden");
50297         }
50298         //if(c.titlebar !== false){
50299             if((!c.titlebar && !c.title) || c.titlebar === false){
50300                 this.titleEl.hide();
50301             }else{
50302                 this.titleEl.show();
50303                 if(c.title){
50304                     this.titleTextEl.innerHTML = c.title;
50305                 }
50306             }
50307         //}
50308         this.duration = c.duration || .30;
50309         this.slideDuration = c.slideDuration || .45;
50310         this.config = c;
50311         if(c.collapsed){
50312             this.collapse(true);
50313         }
50314         if(c.hidden){
50315             this.hide();
50316         }
50317     },
50318     /**
50319      * Returns true if this region is currently visible.
50320      * @return {Boolean}
50321      */
50322     isVisible : function(){
50323         return this.visible;
50324     },
50325
50326     /**
50327      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50328      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50329      */
50330     setCollapsedTitle : function(title){
50331         title = title || "&#160;";
50332         if(this.collapsedTitleTextEl){
50333             this.collapsedTitleTextEl.innerHTML = title;
50334         }
50335     },
50336
50337     getBox : function(){
50338         var b;
50339         if(!this.collapsed){
50340             b = this.el.getBox(false, true);
50341         }else{
50342             b = this.collapsedEl.getBox(false, true);
50343         }
50344         return b;
50345     },
50346
50347     getMargins : function(){
50348         return this.collapsed ? this.cmargins : this.margins;
50349     },
50350
50351     highlight : function(){
50352         this.el.addClass("x-layout-panel-dragover");
50353     },
50354
50355     unhighlight : function(){
50356         this.el.removeClass("x-layout-panel-dragover");
50357     },
50358
50359     updateBox : function(box){
50360         this.box = box;
50361         if(!this.collapsed){
50362             this.el.dom.style.left = box.x + "px";
50363             this.el.dom.style.top = box.y + "px";
50364             this.updateBody(box.width, box.height);
50365         }else{
50366             this.collapsedEl.dom.style.left = box.x + "px";
50367             this.collapsedEl.dom.style.top = box.y + "px";
50368             this.collapsedEl.setSize(box.width, box.height);
50369         }
50370         if(this.tabs){
50371             this.tabs.autoSizeTabs();
50372         }
50373     },
50374
50375     updateBody : function(w, h){
50376         if(w !== null){
50377             this.el.setWidth(w);
50378             w -= this.el.getBorderWidth("rl");
50379             if(this.config.adjustments){
50380                 w += this.config.adjustments[0];
50381             }
50382         }
50383         if(h !== null){
50384             this.el.setHeight(h);
50385             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50386             h -= this.el.getBorderWidth("tb");
50387             if(this.config.adjustments){
50388                 h += this.config.adjustments[1];
50389             }
50390             this.bodyEl.setHeight(h);
50391             if(this.tabs){
50392                 h = this.tabs.syncHeight(h);
50393             }
50394         }
50395         if(this.panelSize){
50396             w = w !== null ? w : this.panelSize.width;
50397             h = h !== null ? h : this.panelSize.height;
50398         }
50399         if(this.activePanel){
50400             var el = this.activePanel.getEl();
50401             w = w !== null ? w : el.getWidth();
50402             h = h !== null ? h : el.getHeight();
50403             this.panelSize = {width: w, height: h};
50404             this.activePanel.setSize(w, h);
50405         }
50406         if(Roo.isIE && this.tabs){
50407             this.tabs.el.repaint();
50408         }
50409     },
50410
50411     /**
50412      * Returns the container element for this region.
50413      * @return {Roo.Element}
50414      */
50415     getEl : function(){
50416         return this.el;
50417     },
50418
50419     /**
50420      * Hides this region.
50421      */
50422     hide : function(){
50423         if(!this.collapsed){
50424             this.el.dom.style.left = "-2000px";
50425             this.el.hide();
50426         }else{
50427             this.collapsedEl.dom.style.left = "-2000px";
50428             this.collapsedEl.hide();
50429         }
50430         this.visible = false;
50431         this.fireEvent("visibilitychange", this, false);
50432     },
50433
50434     /**
50435      * Shows this region if it was previously hidden.
50436      */
50437     show : function(){
50438         if(!this.collapsed){
50439             this.el.show();
50440         }else{
50441             this.collapsedEl.show();
50442         }
50443         this.visible = true;
50444         this.fireEvent("visibilitychange", this, true);
50445     },
50446
50447     closeClicked : function(){
50448         if(this.activePanel){
50449             this.remove(this.activePanel);
50450         }
50451     },
50452
50453     collapseClick : function(e){
50454         if(this.isSlid){
50455            e.stopPropagation();
50456            this.slideIn();
50457         }else{
50458            e.stopPropagation();
50459            this.slideOut();
50460         }
50461     },
50462
50463     /**
50464      * Collapses this region.
50465      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50466      */
50467     collapse : function(skipAnim){
50468         if(this.collapsed) return;
50469         this.collapsed = true;
50470         if(this.split){
50471             this.split.el.hide();
50472         }
50473         if(this.config.animate && skipAnim !== true){
50474             this.fireEvent("invalidated", this);
50475             this.animateCollapse();
50476         }else{
50477             this.el.setLocation(-20000,-20000);
50478             this.el.hide();
50479             this.collapsedEl.show();
50480             this.fireEvent("collapsed", this);
50481             this.fireEvent("invalidated", this);
50482         }
50483     },
50484
50485     animateCollapse : function(){
50486         // overridden
50487     },
50488
50489     /**
50490      * Expands this region if it was previously collapsed.
50491      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50492      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50493      */
50494     expand : function(e, skipAnim){
50495         if(e) e.stopPropagation();
50496         if(!this.collapsed || this.el.hasActiveFx()) return;
50497         if(this.isSlid){
50498             this.afterSlideIn();
50499             skipAnim = true;
50500         }
50501         this.collapsed = false;
50502         if(this.config.animate && skipAnim !== true){
50503             this.animateExpand();
50504         }else{
50505             this.el.show();
50506             if(this.split){
50507                 this.split.el.show();
50508             }
50509             this.collapsedEl.setLocation(-2000,-2000);
50510             this.collapsedEl.hide();
50511             this.fireEvent("invalidated", this);
50512             this.fireEvent("expanded", this);
50513         }
50514     },
50515
50516     animateExpand : function(){
50517         // overridden
50518     },
50519
50520     initTabs : function()
50521     {
50522         this.bodyEl.setStyle("overflow", "hidden");
50523         var ts = new Roo.TabPanel(
50524                 this.bodyEl.dom,
50525                 {
50526                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50527                     disableTooltips: this.config.disableTabTips,
50528                     toolbar : this.config.toolbar
50529                 }
50530         );
50531         if(this.config.hideTabs){
50532             ts.stripWrap.setDisplayed(false);
50533         }
50534         this.tabs = ts;
50535         ts.resizeTabs = this.config.resizeTabs === true;
50536         ts.minTabWidth = this.config.minTabWidth || 40;
50537         ts.maxTabWidth = this.config.maxTabWidth || 250;
50538         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50539         ts.monitorResize = false;
50540         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50541         ts.bodyEl.addClass('x-layout-tabs-body');
50542         this.panels.each(this.initPanelAsTab, this);
50543     },
50544
50545     initPanelAsTab : function(panel){
50546         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50547                     this.config.closeOnTab && panel.isClosable());
50548         if(panel.tabTip !== undefined){
50549             ti.setTooltip(panel.tabTip);
50550         }
50551         ti.on("activate", function(){
50552               this.setActivePanel(panel);
50553         }, this);
50554         if(this.config.closeOnTab){
50555             ti.on("beforeclose", function(t, e){
50556                 e.cancel = true;
50557                 this.remove(panel);
50558             }, this);
50559         }
50560         return ti;
50561     },
50562
50563     updatePanelTitle : function(panel, title){
50564         if(this.activePanel == panel){
50565             this.updateTitle(title);
50566         }
50567         if(this.tabs){
50568             var ti = this.tabs.getTab(panel.getEl().id);
50569             ti.setText(title);
50570             if(panel.tabTip !== undefined){
50571                 ti.setTooltip(panel.tabTip);
50572             }
50573         }
50574     },
50575
50576     updateTitle : function(title){
50577         if(this.titleTextEl && !this.config.title){
50578             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50579         }
50580     },
50581
50582     setActivePanel : function(panel){
50583         panel = this.getPanel(panel);
50584         if(this.activePanel && this.activePanel != panel){
50585             this.activePanel.setActiveState(false);
50586         }
50587         this.activePanel = panel;
50588         panel.setActiveState(true);
50589         if(this.panelSize){
50590             panel.setSize(this.panelSize.width, this.panelSize.height);
50591         }
50592         if(this.closeBtn){
50593             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50594         }
50595         this.updateTitle(panel.getTitle());
50596         if(this.tabs){
50597             this.fireEvent("invalidated", this);
50598         }
50599         this.fireEvent("panelactivated", this, panel);
50600     },
50601
50602     /**
50603      * Shows the specified panel.
50604      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50605      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50606      */
50607     showPanel : function(panel)
50608     {
50609         panel = this.getPanel(panel);
50610         if(panel){
50611             if(this.tabs){
50612                 var tab = this.tabs.getTab(panel.getEl().id);
50613                 if(tab.isHidden()){
50614                     this.tabs.unhideTab(tab.id);
50615                 }
50616                 tab.activate();
50617             }else{
50618                 this.setActivePanel(panel);
50619             }
50620         }
50621         return panel;
50622     },
50623
50624     /**
50625      * Get the active panel for this region.
50626      * @return {Roo.ContentPanel} The active panel or null
50627      */
50628     getActivePanel : function(){
50629         return this.activePanel;
50630     },
50631
50632     validateVisibility : function(){
50633         if(this.panels.getCount() < 1){
50634             this.updateTitle("&#160;");
50635             this.closeBtn.hide();
50636             this.hide();
50637         }else{
50638             if(!this.isVisible()){
50639                 this.show();
50640             }
50641         }
50642     },
50643
50644     /**
50645      * Adds the passed ContentPanel(s) to this region.
50646      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50647      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50648      */
50649     add : function(panel){
50650         if(arguments.length > 1){
50651             for(var i = 0, len = arguments.length; i < len; i++) {
50652                 this.add(arguments[i]);
50653             }
50654             return null;
50655         }
50656         if(this.hasPanel(panel)){
50657             this.showPanel(panel);
50658             return panel;
50659         }
50660         panel.setRegion(this);
50661         this.panels.add(panel);
50662         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50663             this.bodyEl.dom.appendChild(panel.getEl().dom);
50664             if(panel.background !== true){
50665                 this.setActivePanel(panel);
50666             }
50667             this.fireEvent("paneladded", this, panel);
50668             return panel;
50669         }
50670         if(!this.tabs){
50671             this.initTabs();
50672         }else{
50673             this.initPanelAsTab(panel);
50674         }
50675         if(panel.background !== true){
50676             this.tabs.activate(panel.getEl().id);
50677         }
50678         this.fireEvent("paneladded", this, panel);
50679         return panel;
50680     },
50681
50682     /**
50683      * Hides the tab for the specified panel.
50684      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50685      */
50686     hidePanel : function(panel){
50687         if(this.tabs && (panel = this.getPanel(panel))){
50688             this.tabs.hideTab(panel.getEl().id);
50689         }
50690     },
50691
50692     /**
50693      * Unhides the tab for a previously hidden panel.
50694      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50695      */
50696     unhidePanel : function(panel){
50697         if(this.tabs && (panel = this.getPanel(panel))){
50698             this.tabs.unhideTab(panel.getEl().id);
50699         }
50700     },
50701
50702     clearPanels : function(){
50703         while(this.panels.getCount() > 0){
50704              this.remove(this.panels.first());
50705         }
50706     },
50707
50708     /**
50709      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50710      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50711      * @param {Boolean} preservePanel Overrides the config preservePanel option
50712      * @return {Roo.ContentPanel} The panel that was removed
50713      */
50714     remove : function(panel, preservePanel){
50715         panel = this.getPanel(panel);
50716         if(!panel){
50717             return null;
50718         }
50719         var e = {};
50720         this.fireEvent("beforeremove", this, panel, e);
50721         if(e.cancel === true){
50722             return null;
50723         }
50724         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50725         var panelId = panel.getId();
50726         this.panels.removeKey(panelId);
50727         if(preservePanel){
50728             document.body.appendChild(panel.getEl().dom);
50729         }
50730         if(this.tabs){
50731             this.tabs.removeTab(panel.getEl().id);
50732         }else if (!preservePanel){
50733             this.bodyEl.dom.removeChild(panel.getEl().dom);
50734         }
50735         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50736             var p = this.panels.first();
50737             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50738             tempEl.appendChild(p.getEl().dom);
50739             this.bodyEl.update("");
50740             this.bodyEl.dom.appendChild(p.getEl().dom);
50741             tempEl = null;
50742             this.updateTitle(p.getTitle());
50743             this.tabs = null;
50744             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50745             this.setActivePanel(p);
50746         }
50747         panel.setRegion(null);
50748         if(this.activePanel == panel){
50749             this.activePanel = null;
50750         }
50751         if(this.config.autoDestroy !== false && preservePanel !== true){
50752             try{panel.destroy();}catch(e){}
50753         }
50754         this.fireEvent("panelremoved", this, panel);
50755         return panel;
50756     },
50757
50758     /**
50759      * Returns the TabPanel component used by this region
50760      * @return {Roo.TabPanel}
50761      */
50762     getTabs : function(){
50763         return this.tabs;
50764     },
50765
50766     createTool : function(parentEl, className){
50767         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50768             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50769         btn.addClassOnOver("x-layout-tools-button-over");
50770         return btn;
50771     }
50772 });/*
50773  * Based on:
50774  * Ext JS Library 1.1.1
50775  * Copyright(c) 2006-2007, Ext JS, LLC.
50776  *
50777  * Originally Released Under LGPL - original licence link has changed is not relivant.
50778  *
50779  * Fork - LGPL
50780  * <script type="text/javascript">
50781  */
50782  
50783
50784
50785 /**
50786  * @class Roo.SplitLayoutRegion
50787  * @extends Roo.LayoutRegion
50788  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50789  */
50790 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50791     this.cursor = cursor;
50792     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50793 };
50794
50795 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50796     splitTip : "Drag to resize.",
50797     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50798     useSplitTips : false,
50799
50800     applyConfig : function(config){
50801         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50802         if(config.split){
50803             if(!this.split){
50804                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50805                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50806                 /** The SplitBar for this region 
50807                 * @type Roo.SplitBar */
50808                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50809                 this.split.on("moved", this.onSplitMove, this);
50810                 this.split.useShim = config.useShim === true;
50811                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50812                 if(this.useSplitTips){
50813                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50814                 }
50815                 if(config.collapsible){
50816                     this.split.el.on("dblclick", this.collapse,  this);
50817                 }
50818             }
50819             if(typeof config.minSize != "undefined"){
50820                 this.split.minSize = config.minSize;
50821             }
50822             if(typeof config.maxSize != "undefined"){
50823                 this.split.maxSize = config.maxSize;
50824             }
50825             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50826                 this.hideSplitter();
50827             }
50828         }
50829     },
50830
50831     getHMaxSize : function(){
50832          var cmax = this.config.maxSize || 10000;
50833          var center = this.mgr.getRegion("center");
50834          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50835     },
50836
50837     getVMaxSize : function(){
50838          var cmax = this.config.maxSize || 10000;
50839          var center = this.mgr.getRegion("center");
50840          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50841     },
50842
50843     onSplitMove : function(split, newSize){
50844         this.fireEvent("resized", this, newSize);
50845     },
50846     
50847     /** 
50848      * Returns the {@link Roo.SplitBar} for this region.
50849      * @return {Roo.SplitBar}
50850      */
50851     getSplitBar : function(){
50852         return this.split;
50853     },
50854     
50855     hide : function(){
50856         this.hideSplitter();
50857         Roo.SplitLayoutRegion.superclass.hide.call(this);
50858     },
50859
50860     hideSplitter : function(){
50861         if(this.split){
50862             this.split.el.setLocation(-2000,-2000);
50863             this.split.el.hide();
50864         }
50865     },
50866
50867     show : function(){
50868         if(this.split){
50869             this.split.el.show();
50870         }
50871         Roo.SplitLayoutRegion.superclass.show.call(this);
50872     },
50873     
50874     beforeSlide: function(){
50875         if(Roo.isGecko){// firefox overflow auto bug workaround
50876             this.bodyEl.clip();
50877             if(this.tabs) this.tabs.bodyEl.clip();
50878             if(this.activePanel){
50879                 this.activePanel.getEl().clip();
50880                 
50881                 if(this.activePanel.beforeSlide){
50882                     this.activePanel.beforeSlide();
50883                 }
50884             }
50885         }
50886     },
50887     
50888     afterSlide : function(){
50889         if(Roo.isGecko){// firefox overflow auto bug workaround
50890             this.bodyEl.unclip();
50891             if(this.tabs) this.tabs.bodyEl.unclip();
50892             if(this.activePanel){
50893                 this.activePanel.getEl().unclip();
50894                 if(this.activePanel.afterSlide){
50895                     this.activePanel.afterSlide();
50896                 }
50897             }
50898         }
50899     },
50900
50901     initAutoHide : function(){
50902         if(this.autoHide !== false){
50903             if(!this.autoHideHd){
50904                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50905                 this.autoHideHd = {
50906                     "mouseout": function(e){
50907                         if(!e.within(this.el, true)){
50908                             st.delay(500);
50909                         }
50910                     },
50911                     "mouseover" : function(e){
50912                         st.cancel();
50913                     },
50914                     scope : this
50915                 };
50916             }
50917             this.el.on(this.autoHideHd);
50918         }
50919     },
50920
50921     clearAutoHide : function(){
50922         if(this.autoHide !== false){
50923             this.el.un("mouseout", this.autoHideHd.mouseout);
50924             this.el.un("mouseover", this.autoHideHd.mouseover);
50925         }
50926     },
50927
50928     clearMonitor : function(){
50929         Roo.get(document).un("click", this.slideInIf, this);
50930     },
50931
50932     // these names are backwards but not changed for compat
50933     slideOut : function(){
50934         if(this.isSlid || this.el.hasActiveFx()){
50935             return;
50936         }
50937         this.isSlid = true;
50938         if(this.collapseBtn){
50939             this.collapseBtn.hide();
50940         }
50941         this.closeBtnState = this.closeBtn.getStyle('display');
50942         this.closeBtn.hide();
50943         if(this.stickBtn){
50944             this.stickBtn.show();
50945         }
50946         this.el.show();
50947         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50948         this.beforeSlide();
50949         this.el.setStyle("z-index", 10001);
50950         this.el.slideIn(this.getSlideAnchor(), {
50951             callback: function(){
50952                 this.afterSlide();
50953                 this.initAutoHide();
50954                 Roo.get(document).on("click", this.slideInIf, this);
50955                 this.fireEvent("slideshow", this);
50956             },
50957             scope: this,
50958             block: true
50959         });
50960     },
50961
50962     afterSlideIn : function(){
50963         this.clearAutoHide();
50964         this.isSlid = false;
50965         this.clearMonitor();
50966         this.el.setStyle("z-index", "");
50967         if(this.collapseBtn){
50968             this.collapseBtn.show();
50969         }
50970         this.closeBtn.setStyle('display', this.closeBtnState);
50971         if(this.stickBtn){
50972             this.stickBtn.hide();
50973         }
50974         this.fireEvent("slidehide", this);
50975     },
50976
50977     slideIn : function(cb){
50978         if(!this.isSlid || this.el.hasActiveFx()){
50979             Roo.callback(cb);
50980             return;
50981         }
50982         this.isSlid = false;
50983         this.beforeSlide();
50984         this.el.slideOut(this.getSlideAnchor(), {
50985             callback: function(){
50986                 this.el.setLeftTop(-10000, -10000);
50987                 this.afterSlide();
50988                 this.afterSlideIn();
50989                 Roo.callback(cb);
50990             },
50991             scope: this,
50992             block: true
50993         });
50994     },
50995     
50996     slideInIf : function(e){
50997         if(!e.within(this.el)){
50998             this.slideIn();
50999         }
51000     },
51001
51002     animateCollapse : function(){
51003         this.beforeSlide();
51004         this.el.setStyle("z-index", 20000);
51005         var anchor = this.getSlideAnchor();
51006         this.el.slideOut(anchor, {
51007             callback : function(){
51008                 this.el.setStyle("z-index", "");
51009                 this.collapsedEl.slideIn(anchor, {duration:.3});
51010                 this.afterSlide();
51011                 this.el.setLocation(-10000,-10000);
51012                 this.el.hide();
51013                 this.fireEvent("collapsed", this);
51014             },
51015             scope: this,
51016             block: true
51017         });
51018     },
51019
51020     animateExpand : function(){
51021         this.beforeSlide();
51022         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51023         this.el.setStyle("z-index", 20000);
51024         this.collapsedEl.hide({
51025             duration:.1
51026         });
51027         this.el.slideIn(this.getSlideAnchor(), {
51028             callback : function(){
51029                 this.el.setStyle("z-index", "");
51030                 this.afterSlide();
51031                 if(this.split){
51032                     this.split.el.show();
51033                 }
51034                 this.fireEvent("invalidated", this);
51035                 this.fireEvent("expanded", this);
51036             },
51037             scope: this,
51038             block: true
51039         });
51040     },
51041
51042     anchors : {
51043         "west" : "left",
51044         "east" : "right",
51045         "north" : "top",
51046         "south" : "bottom"
51047     },
51048
51049     sanchors : {
51050         "west" : "l",
51051         "east" : "r",
51052         "north" : "t",
51053         "south" : "b"
51054     },
51055
51056     canchors : {
51057         "west" : "tl-tr",
51058         "east" : "tr-tl",
51059         "north" : "tl-bl",
51060         "south" : "bl-tl"
51061     },
51062
51063     getAnchor : function(){
51064         return this.anchors[this.position];
51065     },
51066
51067     getCollapseAnchor : function(){
51068         return this.canchors[this.position];
51069     },
51070
51071     getSlideAnchor : function(){
51072         return this.sanchors[this.position];
51073     },
51074
51075     getAlignAdj : function(){
51076         var cm = this.cmargins;
51077         switch(this.position){
51078             case "west":
51079                 return [0, 0];
51080             break;
51081             case "east":
51082                 return [0, 0];
51083             break;
51084             case "north":
51085                 return [0, 0];
51086             break;
51087             case "south":
51088                 return [0, 0];
51089             break;
51090         }
51091     },
51092
51093     getExpandAdj : function(){
51094         var c = this.collapsedEl, cm = this.cmargins;
51095         switch(this.position){
51096             case "west":
51097                 return [-(cm.right+c.getWidth()+cm.left), 0];
51098             break;
51099             case "east":
51100                 return [cm.right+c.getWidth()+cm.left, 0];
51101             break;
51102             case "north":
51103                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51104             break;
51105             case "south":
51106                 return [0, cm.top+cm.bottom+c.getHeight()];
51107             break;
51108         }
51109     }
51110 });/*
51111  * Based on:
51112  * Ext JS Library 1.1.1
51113  * Copyright(c) 2006-2007, Ext JS, LLC.
51114  *
51115  * Originally Released Under LGPL - original licence link has changed is not relivant.
51116  *
51117  * Fork - LGPL
51118  * <script type="text/javascript">
51119  */
51120 /*
51121  * These classes are private internal classes
51122  */
51123 Roo.CenterLayoutRegion = function(mgr, config){
51124     Roo.LayoutRegion.call(this, mgr, config, "center");
51125     this.visible = true;
51126     this.minWidth = config.minWidth || 20;
51127     this.minHeight = config.minHeight || 20;
51128 };
51129
51130 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51131     hide : function(){
51132         // center panel can't be hidden
51133     },
51134     
51135     show : function(){
51136         // center panel can't be hidden
51137     },
51138     
51139     getMinWidth: function(){
51140         return this.minWidth;
51141     },
51142     
51143     getMinHeight: function(){
51144         return this.minHeight;
51145     }
51146 });
51147
51148
51149 Roo.NorthLayoutRegion = function(mgr, config){
51150     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51151     if(this.split){
51152         this.split.placement = Roo.SplitBar.TOP;
51153         this.split.orientation = Roo.SplitBar.VERTICAL;
51154         this.split.el.addClass("x-layout-split-v");
51155     }
51156     var size = config.initialSize || config.height;
51157     if(typeof size != "undefined"){
51158         this.el.setHeight(size);
51159     }
51160 };
51161 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51162     orientation: Roo.SplitBar.VERTICAL,
51163     getBox : function(){
51164         if(this.collapsed){
51165             return this.collapsedEl.getBox();
51166         }
51167         var box = this.el.getBox();
51168         if(this.split){
51169             box.height += this.split.el.getHeight();
51170         }
51171         return box;
51172     },
51173     
51174     updateBox : function(box){
51175         if(this.split && !this.collapsed){
51176             box.height -= this.split.el.getHeight();
51177             this.split.el.setLeft(box.x);
51178             this.split.el.setTop(box.y+box.height);
51179             this.split.el.setWidth(box.width);
51180         }
51181         if(this.collapsed){
51182             this.updateBody(box.width, null);
51183         }
51184         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51185     }
51186 });
51187
51188 Roo.SouthLayoutRegion = function(mgr, config){
51189     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51190     if(this.split){
51191         this.split.placement = Roo.SplitBar.BOTTOM;
51192         this.split.orientation = Roo.SplitBar.VERTICAL;
51193         this.split.el.addClass("x-layout-split-v");
51194     }
51195     var size = config.initialSize || config.height;
51196     if(typeof size != "undefined"){
51197         this.el.setHeight(size);
51198     }
51199 };
51200 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51201     orientation: Roo.SplitBar.VERTICAL,
51202     getBox : function(){
51203         if(this.collapsed){
51204             return this.collapsedEl.getBox();
51205         }
51206         var box = this.el.getBox();
51207         if(this.split){
51208             var sh = this.split.el.getHeight();
51209             box.height += sh;
51210             box.y -= sh;
51211         }
51212         return box;
51213     },
51214     
51215     updateBox : function(box){
51216         if(this.split && !this.collapsed){
51217             var sh = this.split.el.getHeight();
51218             box.height -= sh;
51219             box.y += sh;
51220             this.split.el.setLeft(box.x);
51221             this.split.el.setTop(box.y-sh);
51222             this.split.el.setWidth(box.width);
51223         }
51224         if(this.collapsed){
51225             this.updateBody(box.width, null);
51226         }
51227         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51228     }
51229 });
51230
51231 Roo.EastLayoutRegion = function(mgr, config){
51232     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51233     if(this.split){
51234         this.split.placement = Roo.SplitBar.RIGHT;
51235         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51236         this.split.el.addClass("x-layout-split-h");
51237     }
51238     var size = config.initialSize || config.width;
51239     if(typeof size != "undefined"){
51240         this.el.setWidth(size);
51241     }
51242 };
51243 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51244     orientation: Roo.SplitBar.HORIZONTAL,
51245     getBox : function(){
51246         if(this.collapsed){
51247             return this.collapsedEl.getBox();
51248         }
51249         var box = this.el.getBox();
51250         if(this.split){
51251             var sw = this.split.el.getWidth();
51252             box.width += sw;
51253             box.x -= sw;
51254         }
51255         return box;
51256     },
51257
51258     updateBox : function(box){
51259         if(this.split && !this.collapsed){
51260             var sw = this.split.el.getWidth();
51261             box.width -= sw;
51262             this.split.el.setLeft(box.x);
51263             this.split.el.setTop(box.y);
51264             this.split.el.setHeight(box.height);
51265             box.x += sw;
51266         }
51267         if(this.collapsed){
51268             this.updateBody(null, box.height);
51269         }
51270         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51271     }
51272 });
51273
51274 Roo.WestLayoutRegion = function(mgr, config){
51275     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51276     if(this.split){
51277         this.split.placement = Roo.SplitBar.LEFT;
51278         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51279         this.split.el.addClass("x-layout-split-h");
51280     }
51281     var size = config.initialSize || config.width;
51282     if(typeof size != "undefined"){
51283         this.el.setWidth(size);
51284     }
51285 };
51286 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51287     orientation: Roo.SplitBar.HORIZONTAL,
51288     getBox : function(){
51289         if(this.collapsed){
51290             return this.collapsedEl.getBox();
51291         }
51292         var box = this.el.getBox();
51293         if(this.split){
51294             box.width += this.split.el.getWidth();
51295         }
51296         return box;
51297     },
51298     
51299     updateBox : function(box){
51300         if(this.split && !this.collapsed){
51301             var sw = this.split.el.getWidth();
51302             box.width -= sw;
51303             this.split.el.setLeft(box.x+box.width);
51304             this.split.el.setTop(box.y);
51305             this.split.el.setHeight(box.height);
51306         }
51307         if(this.collapsed){
51308             this.updateBody(null, box.height);
51309         }
51310         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51311     }
51312 });
51313 /*
51314  * Based on:
51315  * Ext JS Library 1.1.1
51316  * Copyright(c) 2006-2007, Ext JS, LLC.
51317  *
51318  * Originally Released Under LGPL - original licence link has changed is not relivant.
51319  *
51320  * Fork - LGPL
51321  * <script type="text/javascript">
51322  */
51323  
51324  
51325 /*
51326  * Private internal class for reading and applying state
51327  */
51328 Roo.LayoutStateManager = function(layout){
51329      // default empty state
51330      this.state = {
51331         north: {},
51332         south: {},
51333         east: {},
51334         west: {}       
51335     };
51336 };
51337
51338 Roo.LayoutStateManager.prototype = {
51339     init : function(layout, provider){
51340         this.provider = provider;
51341         var state = provider.get(layout.id+"-layout-state");
51342         if(state){
51343             var wasUpdating = layout.isUpdating();
51344             if(!wasUpdating){
51345                 layout.beginUpdate();
51346             }
51347             for(var key in state){
51348                 if(typeof state[key] != "function"){
51349                     var rstate = state[key];
51350                     var r = layout.getRegion(key);
51351                     if(r && rstate){
51352                         if(rstate.size){
51353                             r.resizeTo(rstate.size);
51354                         }
51355                         if(rstate.collapsed == true){
51356                             r.collapse(true);
51357                         }else{
51358                             r.expand(null, true);
51359                         }
51360                     }
51361                 }
51362             }
51363             if(!wasUpdating){
51364                 layout.endUpdate();
51365             }
51366             this.state = state; 
51367         }
51368         this.layout = layout;
51369         layout.on("regionresized", this.onRegionResized, this);
51370         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51371         layout.on("regionexpanded", this.onRegionExpanded, this);
51372     },
51373     
51374     storeState : function(){
51375         this.provider.set(this.layout.id+"-layout-state", this.state);
51376     },
51377     
51378     onRegionResized : function(region, newSize){
51379         this.state[region.getPosition()].size = newSize;
51380         this.storeState();
51381     },
51382     
51383     onRegionCollapsed : function(region){
51384         this.state[region.getPosition()].collapsed = true;
51385         this.storeState();
51386     },
51387     
51388     onRegionExpanded : function(region){
51389         this.state[region.getPosition()].collapsed = false;
51390         this.storeState();
51391     }
51392 };/*
51393  * Based on:
51394  * Ext JS Library 1.1.1
51395  * Copyright(c) 2006-2007, Ext JS, LLC.
51396  *
51397  * Originally Released Under LGPL - original licence link has changed is not relivant.
51398  *
51399  * Fork - LGPL
51400  * <script type="text/javascript">
51401  */
51402 /**
51403  * @class Roo.ContentPanel
51404  * @extends Roo.util.Observable
51405  * A basic ContentPanel element.
51406  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51407  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51408  * @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
51409  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51410  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51411  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51412  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51413  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51414  * @cfg {String} title          The title for this panel
51415  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51416  * @cfg {String} url            Calls {@link #setUrl} with this value
51417  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51418  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51419  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51420  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51421
51422  * @constructor
51423  * Create a new ContentPanel.
51424  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51425  * @param {String/Object} config A string to set only the title or a config object
51426  * @param {String} content (optional) Set the HTML content for this panel
51427  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51428  */
51429 Roo.ContentPanel = function(el, config, content){
51430     
51431      
51432     /*
51433     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51434         config = el;
51435         el = Roo.id();
51436     }
51437     if (config && config.parentLayout) { 
51438         el = config.parentLayout.el.createChild(); 
51439     }
51440     */
51441     if(el.autoCreate){ // xtype is available if this is called from factory
51442         config = el;
51443         el = Roo.id();
51444     }
51445     this.el = Roo.get(el);
51446     if(!this.el && config && config.autoCreate){
51447         if(typeof config.autoCreate == "object"){
51448             if(!config.autoCreate.id){
51449                 config.autoCreate.id = config.id||el;
51450             }
51451             this.el = Roo.DomHelper.append(document.body,
51452                         config.autoCreate, true);
51453         }else{
51454             this.el = Roo.DomHelper.append(document.body,
51455                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51456         }
51457     }
51458     this.closable = false;
51459     this.loaded = false;
51460     this.active = false;
51461     if(typeof config == "string"){
51462         this.title = config;
51463     }else{
51464         Roo.apply(this, config);
51465     }
51466     
51467     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51468         this.wrapEl = this.el.wrap();
51469         this.toolbar.container = this.el.insertSibling(false, 'before');
51470         this.toolbar = new Roo.Toolbar(this.toolbar);
51471     }
51472     
51473     // xtype created footer. - not sure if will work as we normally have to render first..
51474     if (this.footer && !this.footer.el && this.footer.xtype) {
51475         if (!this.wrapEl) {
51476             this.wrapEl = this.el.wrap();
51477         }
51478     
51479         this.footer.container = this.wrapEl.createChild();
51480          
51481         this.footer = Roo.factory(this.footer, Roo);
51482         
51483     }
51484     
51485     if(this.resizeEl){
51486         this.resizeEl = Roo.get(this.resizeEl, true);
51487     }else{
51488         this.resizeEl = this.el;
51489     }
51490     // handle view.xtype
51491     
51492  
51493     
51494     
51495     this.addEvents({
51496         /**
51497          * @event activate
51498          * Fires when this panel is activated. 
51499          * @param {Roo.ContentPanel} this
51500          */
51501         "activate" : true,
51502         /**
51503          * @event deactivate
51504          * Fires when this panel is activated. 
51505          * @param {Roo.ContentPanel} this
51506          */
51507         "deactivate" : true,
51508
51509         /**
51510          * @event resize
51511          * Fires when this panel is resized if fitToFrame is true.
51512          * @param {Roo.ContentPanel} this
51513          * @param {Number} width The width after any component adjustments
51514          * @param {Number} height The height after any component adjustments
51515          */
51516         "resize" : true,
51517         
51518          /**
51519          * @event render
51520          * Fires when this tab is created
51521          * @param {Roo.ContentPanel} this
51522          */
51523         "render" : true
51524         
51525         
51526         
51527     });
51528     
51529
51530     
51531     
51532     if(this.autoScroll){
51533         this.resizeEl.setStyle("overflow", "auto");
51534     } else {
51535         // fix randome scrolling
51536         this.el.on('scroll', function() {
51537             Roo.log('fix random scolling');
51538             this.scrollTo('top',0); 
51539         });
51540     }
51541     content = content || this.content;
51542     if(content){
51543         this.setContent(content);
51544     }
51545     if(config && config.url){
51546         this.setUrl(this.url, this.params, this.loadOnce);
51547     }
51548     
51549     
51550     
51551     Roo.ContentPanel.superclass.constructor.call(this);
51552     
51553     if (this.view && typeof(this.view.xtype) != 'undefined') {
51554         this.view.el = this.el.appendChild(document.createElement("div"));
51555         this.view = Roo.factory(this.view); 
51556         this.view.render  &&  this.view.render(false, '');  
51557     }
51558     
51559     
51560     this.fireEvent('render', this);
51561 };
51562
51563 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51564     tabTip:'',
51565     setRegion : function(region){
51566         this.region = region;
51567         if(region){
51568            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51569         }else{
51570            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51571         } 
51572     },
51573     
51574     /**
51575      * Returns the toolbar for this Panel if one was configured. 
51576      * @return {Roo.Toolbar} 
51577      */
51578     getToolbar : function(){
51579         return this.toolbar;
51580     },
51581     
51582     setActiveState : function(active){
51583         this.active = active;
51584         if(!active){
51585             this.fireEvent("deactivate", this);
51586         }else{
51587             this.fireEvent("activate", this);
51588         }
51589     },
51590     /**
51591      * Updates this panel's element
51592      * @param {String} content The new content
51593      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51594     */
51595     setContent : function(content, loadScripts){
51596         this.el.update(content, loadScripts);
51597     },
51598
51599     ignoreResize : function(w, h){
51600         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51601             return true;
51602         }else{
51603             this.lastSize = {width: w, height: h};
51604             return false;
51605         }
51606     },
51607     /**
51608      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51609      * @return {Roo.UpdateManager} The UpdateManager
51610      */
51611     getUpdateManager : function(){
51612         return this.el.getUpdateManager();
51613     },
51614      /**
51615      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51616      * @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:
51617 <pre><code>
51618 panel.load({
51619     url: "your-url.php",
51620     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51621     callback: yourFunction,
51622     scope: yourObject, //(optional scope)
51623     discardUrl: false,
51624     nocache: false,
51625     text: "Loading...",
51626     timeout: 30,
51627     scripts: false
51628 });
51629 </code></pre>
51630      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51631      * 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.
51632      * @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}
51633      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51634      * @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.
51635      * @return {Roo.ContentPanel} this
51636      */
51637     load : function(){
51638         var um = this.el.getUpdateManager();
51639         um.update.apply(um, arguments);
51640         return this;
51641     },
51642
51643
51644     /**
51645      * 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.
51646      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51647      * @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)
51648      * @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)
51649      * @return {Roo.UpdateManager} The UpdateManager
51650      */
51651     setUrl : function(url, params, loadOnce){
51652         if(this.refreshDelegate){
51653             this.removeListener("activate", this.refreshDelegate);
51654         }
51655         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51656         this.on("activate", this.refreshDelegate);
51657         return this.el.getUpdateManager();
51658     },
51659     
51660     _handleRefresh : function(url, params, loadOnce){
51661         if(!loadOnce || !this.loaded){
51662             var updater = this.el.getUpdateManager();
51663             updater.update(url, params, this._setLoaded.createDelegate(this));
51664         }
51665     },
51666     
51667     _setLoaded : function(){
51668         this.loaded = true;
51669     }, 
51670     
51671     /**
51672      * Returns this panel's id
51673      * @return {String} 
51674      */
51675     getId : function(){
51676         return this.el.id;
51677     },
51678     
51679     /** 
51680      * Returns this panel's element - used by regiosn to add.
51681      * @return {Roo.Element} 
51682      */
51683     getEl : function(){
51684         return this.wrapEl || this.el;
51685     },
51686     
51687     adjustForComponents : function(width, height)
51688     {
51689         //Roo.log('adjustForComponents ');
51690         if(this.resizeEl != this.el){
51691             width -= this.el.getFrameWidth('lr');
51692             height -= this.el.getFrameWidth('tb');
51693         }
51694         if(this.toolbar){
51695             var te = this.toolbar.getEl();
51696             height -= te.getHeight();
51697             te.setWidth(width);
51698         }
51699         if(this.footer){
51700             var te = this.footer.getEl();
51701             Roo.log("footer:" + te.getHeight());
51702             
51703             height -= te.getHeight();
51704             te.setWidth(width);
51705         }
51706         
51707         
51708         if(this.adjustments){
51709             width += this.adjustments[0];
51710             height += this.adjustments[1];
51711         }
51712         return {"width": width, "height": height};
51713     },
51714     
51715     setSize : function(width, height){
51716         if(this.fitToFrame && !this.ignoreResize(width, height)){
51717             if(this.fitContainer && this.resizeEl != this.el){
51718                 this.el.setSize(width, height);
51719             }
51720             var size = this.adjustForComponents(width, height);
51721             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51722             this.fireEvent('resize', this, size.width, size.height);
51723         }
51724     },
51725     
51726     /**
51727      * Returns this panel's title
51728      * @return {String} 
51729      */
51730     getTitle : function(){
51731         return this.title;
51732     },
51733     
51734     /**
51735      * Set this panel's title
51736      * @param {String} title
51737      */
51738     setTitle : function(title){
51739         this.title = title;
51740         if(this.region){
51741             this.region.updatePanelTitle(this, title);
51742         }
51743     },
51744     
51745     /**
51746      * Returns true is this panel was configured to be closable
51747      * @return {Boolean} 
51748      */
51749     isClosable : function(){
51750         return this.closable;
51751     },
51752     
51753     beforeSlide : function(){
51754         this.el.clip();
51755         this.resizeEl.clip();
51756     },
51757     
51758     afterSlide : function(){
51759         this.el.unclip();
51760         this.resizeEl.unclip();
51761     },
51762     
51763     /**
51764      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51765      *   Will fail silently if the {@link #setUrl} method has not been called.
51766      *   This does not activate the panel, just updates its content.
51767      */
51768     refresh : function(){
51769         if(this.refreshDelegate){
51770            this.loaded = false;
51771            this.refreshDelegate();
51772         }
51773     },
51774     
51775     /**
51776      * Destroys this panel
51777      */
51778     destroy : function(){
51779         this.el.removeAllListeners();
51780         var tempEl = document.createElement("span");
51781         tempEl.appendChild(this.el.dom);
51782         tempEl.innerHTML = "";
51783         this.el.remove();
51784         this.el = null;
51785     },
51786     
51787     /**
51788      * form - if the content panel contains a form - this is a reference to it.
51789      * @type {Roo.form.Form}
51790      */
51791     form : false,
51792     /**
51793      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51794      *    This contains a reference to it.
51795      * @type {Roo.View}
51796      */
51797     view : false,
51798     
51799       /**
51800      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51801      * <pre><code>
51802
51803 layout.addxtype({
51804        xtype : 'Form',
51805        items: [ .... ]
51806    }
51807 );
51808
51809 </code></pre>
51810      * @param {Object} cfg Xtype definition of item to add.
51811      */
51812     
51813     addxtype : function(cfg) {
51814         // add form..
51815         if (cfg.xtype.match(/^Form$/)) {
51816             
51817             var el;
51818             //if (this.footer) {
51819             //    el = this.footer.container.insertSibling(false, 'before');
51820             //} else {
51821                 el = this.el.createChild();
51822             //}
51823
51824             this.form = new  Roo.form.Form(cfg);
51825             
51826             
51827             if ( this.form.allItems.length) this.form.render(el.dom);
51828             return this.form;
51829         }
51830         // should only have one of theses..
51831         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51832             // views.. should not be just added - used named prop 'view''
51833             
51834             cfg.el = this.el.appendChild(document.createElement("div"));
51835             // factory?
51836             
51837             var ret = new Roo.factory(cfg);
51838              
51839              ret.render && ret.render(false, ''); // render blank..
51840             this.view = ret;
51841             return ret;
51842         }
51843         return false;
51844     }
51845 });
51846
51847 /**
51848  * @class Roo.GridPanel
51849  * @extends Roo.ContentPanel
51850  * @constructor
51851  * Create a new GridPanel.
51852  * @param {Roo.grid.Grid} grid The grid for this panel
51853  * @param {String/Object} config A string to set only the panel's title, or a config object
51854  */
51855 Roo.GridPanel = function(grid, config){
51856     
51857   
51858     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51859         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51860         
51861     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51862     
51863     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51864     
51865     if(this.toolbar){
51866         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51867     }
51868     // xtype created footer. - not sure if will work as we normally have to render first..
51869     if (this.footer && !this.footer.el && this.footer.xtype) {
51870         
51871         this.footer.container = this.grid.getView().getFooterPanel(true);
51872         this.footer.dataSource = this.grid.dataSource;
51873         this.footer = Roo.factory(this.footer, Roo);
51874         
51875     }
51876     
51877     grid.monitorWindowResize = false; // turn off autosizing
51878     grid.autoHeight = false;
51879     grid.autoWidth = false;
51880     this.grid = grid;
51881     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51882 };
51883
51884 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51885     getId : function(){
51886         return this.grid.id;
51887     },
51888     
51889     /**
51890      * Returns the grid for this panel
51891      * @return {Roo.grid.Grid} 
51892      */
51893     getGrid : function(){
51894         return this.grid;    
51895     },
51896     
51897     setSize : function(width, height){
51898         if(!this.ignoreResize(width, height)){
51899             var grid = this.grid;
51900             var size = this.adjustForComponents(width, height);
51901             grid.getGridEl().setSize(size.width, size.height);
51902             grid.autoSize();
51903         }
51904     },
51905     
51906     beforeSlide : function(){
51907         this.grid.getView().scroller.clip();
51908     },
51909     
51910     afterSlide : function(){
51911         this.grid.getView().scroller.unclip();
51912     },
51913     
51914     destroy : function(){
51915         this.grid.destroy();
51916         delete this.grid;
51917         Roo.GridPanel.superclass.destroy.call(this); 
51918     }
51919 });
51920
51921
51922 /**
51923  * @class Roo.NestedLayoutPanel
51924  * @extends Roo.ContentPanel
51925  * @constructor
51926  * Create a new NestedLayoutPanel.
51927  * 
51928  * 
51929  * @param {Roo.BorderLayout} layout The layout for this panel
51930  * @param {String/Object} config A string to set only the title or a config object
51931  */
51932 Roo.NestedLayoutPanel = function(layout, config)
51933 {
51934     // construct with only one argument..
51935     /* FIXME - implement nicer consturctors
51936     if (layout.layout) {
51937         config = layout;
51938         layout = config.layout;
51939         delete config.layout;
51940     }
51941     if (layout.xtype && !layout.getEl) {
51942         // then layout needs constructing..
51943         layout = Roo.factory(layout, Roo);
51944     }
51945     */
51946     
51947     
51948     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51949     
51950     layout.monitorWindowResize = false; // turn off autosizing
51951     this.layout = layout;
51952     this.layout.getEl().addClass("x-layout-nested-layout");
51953     
51954     
51955     
51956     
51957 };
51958
51959 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51960
51961     setSize : function(width, height){
51962         if(!this.ignoreResize(width, height)){
51963             var size = this.adjustForComponents(width, height);
51964             var el = this.layout.getEl();
51965             el.setSize(size.width, size.height);
51966             var touch = el.dom.offsetWidth;
51967             this.layout.layout();
51968             // ie requires a double layout on the first pass
51969             if(Roo.isIE && !this.initialized){
51970                 this.initialized = true;
51971                 this.layout.layout();
51972             }
51973         }
51974     },
51975     
51976     // activate all subpanels if not currently active..
51977     
51978     setActiveState : function(active){
51979         this.active = active;
51980         if(!active){
51981             this.fireEvent("deactivate", this);
51982             return;
51983         }
51984         
51985         this.fireEvent("activate", this);
51986         // not sure if this should happen before or after..
51987         if (!this.layout) {
51988             return; // should not happen..
51989         }
51990         var reg = false;
51991         for (var r in this.layout.regions) {
51992             reg = this.layout.getRegion(r);
51993             if (reg.getActivePanel()) {
51994                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51995                 reg.setActivePanel(reg.getActivePanel());
51996                 continue;
51997             }
51998             if (!reg.panels.length) {
51999                 continue;
52000             }
52001             reg.showPanel(reg.getPanel(0));
52002         }
52003         
52004         
52005         
52006         
52007     },
52008     
52009     /**
52010      * Returns the nested BorderLayout for this panel
52011      * @return {Roo.BorderLayout} 
52012      */
52013     getLayout : function(){
52014         return this.layout;
52015     },
52016     
52017      /**
52018      * Adds a xtype elements to the layout of the nested panel
52019      * <pre><code>
52020
52021 panel.addxtype({
52022        xtype : 'ContentPanel',
52023        region: 'west',
52024        items: [ .... ]
52025    }
52026 );
52027
52028 panel.addxtype({
52029         xtype : 'NestedLayoutPanel',
52030         region: 'west',
52031         layout: {
52032            center: { },
52033            west: { }   
52034         },
52035         items : [ ... list of content panels or nested layout panels.. ]
52036    }
52037 );
52038 </code></pre>
52039      * @param {Object} cfg Xtype definition of item to add.
52040      */
52041     addxtype : function(cfg) {
52042         return this.layout.addxtype(cfg);
52043     
52044     }
52045 });
52046
52047 Roo.ScrollPanel = function(el, config, content){
52048     config = config || {};
52049     config.fitToFrame = true;
52050     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52051     
52052     this.el.dom.style.overflow = "hidden";
52053     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52054     this.el.removeClass("x-layout-inactive-content");
52055     this.el.on("mousewheel", this.onWheel, this);
52056
52057     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52058     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52059     up.unselectable(); down.unselectable();
52060     up.on("click", this.scrollUp, this);
52061     down.on("click", this.scrollDown, this);
52062     up.addClassOnOver("x-scroller-btn-over");
52063     down.addClassOnOver("x-scroller-btn-over");
52064     up.addClassOnClick("x-scroller-btn-click");
52065     down.addClassOnClick("x-scroller-btn-click");
52066     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52067
52068     this.resizeEl = this.el;
52069     this.el = wrap; this.up = up; this.down = down;
52070 };
52071
52072 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52073     increment : 100,
52074     wheelIncrement : 5,
52075     scrollUp : function(){
52076         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52077     },
52078
52079     scrollDown : function(){
52080         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52081     },
52082
52083     afterScroll : function(){
52084         var el = this.resizeEl;
52085         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52086         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52087         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52088     },
52089
52090     setSize : function(){
52091         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52092         this.afterScroll();
52093     },
52094
52095     onWheel : function(e){
52096         var d = e.getWheelDelta();
52097         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52098         this.afterScroll();
52099         e.stopEvent();
52100     },
52101
52102     setContent : function(content, loadScripts){
52103         this.resizeEl.update(content, loadScripts);
52104     }
52105
52106 });
52107
52108
52109
52110
52111
52112
52113
52114
52115
52116 /**
52117  * @class Roo.TreePanel
52118  * @extends Roo.ContentPanel
52119  * @constructor
52120  * Create a new TreePanel. - defaults to fit/scoll contents.
52121  * @param {String/Object} config A string to set only the panel's title, or a config object
52122  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52123  */
52124 Roo.TreePanel = function(config){
52125     var el = config.el;
52126     var tree = config.tree;
52127     delete config.tree; 
52128     delete config.el; // hopefull!
52129     
52130     // wrapper for IE7 strict & safari scroll issue
52131     
52132     var treeEl = el.createChild();
52133     config.resizeEl = treeEl;
52134     
52135     
52136     
52137     Roo.TreePanel.superclass.constructor.call(this, el, config);
52138  
52139  
52140     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52141     //console.log(tree);
52142     this.on('activate', function()
52143     {
52144         if (this.tree.rendered) {
52145             return;
52146         }
52147         //console.log('render tree');
52148         this.tree.render();
52149     });
52150     // this should not be needed.. - it's actually the 'el' that resizes?
52151     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52152     
52153     //this.on('resize',  function (cp, w, h) {
52154     //        this.tree.innerCt.setWidth(w);
52155     //        this.tree.innerCt.setHeight(h);
52156     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52157     //});
52158
52159         
52160     
52161 };
52162
52163 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52164     fitToFrame : true,
52165     autoScroll : true
52166 });
52167
52168
52169
52170
52171
52172
52173
52174
52175
52176
52177
52178 /*
52179  * Based on:
52180  * Ext JS Library 1.1.1
52181  * Copyright(c) 2006-2007, Ext JS, LLC.
52182  *
52183  * Originally Released Under LGPL - original licence link has changed is not relivant.
52184  *
52185  * Fork - LGPL
52186  * <script type="text/javascript">
52187  */
52188  
52189
52190 /**
52191  * @class Roo.ReaderLayout
52192  * @extends Roo.BorderLayout
52193  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52194  * center region containing two nested regions (a top one for a list view and one for item preview below),
52195  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52196  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52197  * expedites the setup of the overall layout and regions for this common application style.
52198  * Example:
52199  <pre><code>
52200 var reader = new Roo.ReaderLayout();
52201 var CP = Roo.ContentPanel;  // shortcut for adding
52202
52203 reader.beginUpdate();
52204 reader.add("north", new CP("north", "North"));
52205 reader.add("west", new CP("west", {title: "West"}));
52206 reader.add("east", new CP("east", {title: "East"}));
52207
52208 reader.regions.listView.add(new CP("listView", "List"));
52209 reader.regions.preview.add(new CP("preview", "Preview"));
52210 reader.endUpdate();
52211 </code></pre>
52212 * @constructor
52213 * Create a new ReaderLayout
52214 * @param {Object} config Configuration options
52215 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52216 * document.body if omitted)
52217 */
52218 Roo.ReaderLayout = function(config, renderTo){
52219     var c = config || {size:{}};
52220     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52221         north: c.north !== false ? Roo.apply({
52222             split:false,
52223             initialSize: 32,
52224             titlebar: false
52225         }, c.north) : false,
52226         west: c.west !== false ? Roo.apply({
52227             split:true,
52228             initialSize: 200,
52229             minSize: 175,
52230             maxSize: 400,
52231             titlebar: true,
52232             collapsible: true,
52233             animate: true,
52234             margins:{left:5,right:0,bottom:5,top:5},
52235             cmargins:{left:5,right:5,bottom:5,top:5}
52236         }, c.west) : false,
52237         east: c.east !== false ? Roo.apply({
52238             split:true,
52239             initialSize: 200,
52240             minSize: 175,
52241             maxSize: 400,
52242             titlebar: true,
52243             collapsible: true,
52244             animate: true,
52245             margins:{left:0,right:5,bottom:5,top:5},
52246             cmargins:{left:5,right:5,bottom:5,top:5}
52247         }, c.east) : false,
52248         center: Roo.apply({
52249             tabPosition: 'top',
52250             autoScroll:false,
52251             closeOnTab: true,
52252             titlebar:false,
52253             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52254         }, c.center)
52255     });
52256
52257     this.el.addClass('x-reader');
52258
52259     this.beginUpdate();
52260
52261     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52262         south: c.preview !== false ? Roo.apply({
52263             split:true,
52264             initialSize: 200,
52265             minSize: 100,
52266             autoScroll:true,
52267             collapsible:true,
52268             titlebar: true,
52269             cmargins:{top:5,left:0, right:0, bottom:0}
52270         }, c.preview) : false,
52271         center: Roo.apply({
52272             autoScroll:false,
52273             titlebar:false,
52274             minHeight:200
52275         }, c.listView)
52276     });
52277     this.add('center', new Roo.NestedLayoutPanel(inner,
52278             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52279
52280     this.endUpdate();
52281
52282     this.regions.preview = inner.getRegion('south');
52283     this.regions.listView = inner.getRegion('center');
52284 };
52285
52286 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52287  * Based on:
52288  * Ext JS Library 1.1.1
52289  * Copyright(c) 2006-2007, Ext JS, LLC.
52290  *
52291  * Originally Released Under LGPL - original licence link has changed is not relivant.
52292  *
52293  * Fork - LGPL
52294  * <script type="text/javascript">
52295  */
52296  
52297 /**
52298  * @class Roo.grid.Grid
52299  * @extends Roo.util.Observable
52300  * This class represents the primary interface of a component based grid control.
52301  * <br><br>Usage:<pre><code>
52302  var grid = new Roo.grid.Grid("my-container-id", {
52303      ds: myDataStore,
52304      cm: myColModel,
52305      selModel: mySelectionModel,
52306      autoSizeColumns: true,
52307      monitorWindowResize: false,
52308      trackMouseOver: true
52309  });
52310  // set any options
52311  grid.render();
52312  * </code></pre>
52313  * <b>Common Problems:</b><br/>
52314  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52315  * element will correct this<br/>
52316  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52317  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52318  * are unpredictable.<br/>
52319  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52320  * grid to calculate dimensions/offsets.<br/>
52321   * @constructor
52322  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52323  * The container MUST have some type of size defined for the grid to fill. The container will be
52324  * automatically set to position relative if it isn't already.
52325  * @param {Object} config A config object that sets properties on this grid.
52326  */
52327 Roo.grid.Grid = function(container, config){
52328         // initialize the container
52329         this.container = Roo.get(container);
52330         this.container.update("");
52331         this.container.setStyle("overflow", "hidden");
52332     this.container.addClass('x-grid-container');
52333
52334     this.id = this.container.id;
52335
52336     Roo.apply(this, config);
52337     // check and correct shorthanded configs
52338     if(this.ds){
52339         this.dataSource = this.ds;
52340         delete this.ds;
52341     }
52342     if(this.cm){
52343         this.colModel = this.cm;
52344         delete this.cm;
52345     }
52346     if(this.sm){
52347         this.selModel = this.sm;
52348         delete this.sm;
52349     }
52350
52351     if (this.selModel) {
52352         this.selModel = Roo.factory(this.selModel, Roo.grid);
52353         this.sm = this.selModel;
52354         this.sm.xmodule = this.xmodule || false;
52355     }
52356     if (typeof(this.colModel.config) == 'undefined') {
52357         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52358         this.cm = this.colModel;
52359         this.cm.xmodule = this.xmodule || false;
52360     }
52361     if (this.dataSource) {
52362         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52363         this.ds = this.dataSource;
52364         this.ds.xmodule = this.xmodule || false;
52365          
52366     }
52367     
52368     
52369     
52370     if(this.width){
52371         this.container.setWidth(this.width);
52372     }
52373
52374     if(this.height){
52375         this.container.setHeight(this.height);
52376     }
52377     /** @private */
52378         this.addEvents({
52379         // raw events
52380         /**
52381          * @event click
52382          * The raw click event for the entire grid.
52383          * @param {Roo.EventObject} e
52384          */
52385         "click" : true,
52386         /**
52387          * @event dblclick
52388          * The raw dblclick event for the entire grid.
52389          * @param {Roo.EventObject} e
52390          */
52391         "dblclick" : true,
52392         /**
52393          * @event contextmenu
52394          * The raw contextmenu event for the entire grid.
52395          * @param {Roo.EventObject} e
52396          */
52397         "contextmenu" : true,
52398         /**
52399          * @event mousedown
52400          * The raw mousedown event for the entire grid.
52401          * @param {Roo.EventObject} e
52402          */
52403         "mousedown" : true,
52404         /**
52405          * @event mouseup
52406          * The raw mouseup event for the entire grid.
52407          * @param {Roo.EventObject} e
52408          */
52409         "mouseup" : true,
52410         /**
52411          * @event mouseover
52412          * The raw mouseover event for the entire grid.
52413          * @param {Roo.EventObject} e
52414          */
52415         "mouseover" : true,
52416         /**
52417          * @event mouseout
52418          * The raw mouseout event for the entire grid.
52419          * @param {Roo.EventObject} e
52420          */
52421         "mouseout" : true,
52422         /**
52423          * @event keypress
52424          * The raw keypress event for the entire grid.
52425          * @param {Roo.EventObject} e
52426          */
52427         "keypress" : true,
52428         /**
52429          * @event keydown
52430          * The raw keydown event for the entire grid.
52431          * @param {Roo.EventObject} e
52432          */
52433         "keydown" : true,
52434
52435         // custom events
52436
52437         /**
52438          * @event cellclick
52439          * Fires when a cell is clicked
52440          * @param {Grid} this
52441          * @param {Number} rowIndex
52442          * @param {Number} columnIndex
52443          * @param {Roo.EventObject} e
52444          */
52445         "cellclick" : true,
52446         /**
52447          * @event celldblclick
52448          * Fires when a cell is double clicked
52449          * @param {Grid} this
52450          * @param {Number} rowIndex
52451          * @param {Number} columnIndex
52452          * @param {Roo.EventObject} e
52453          */
52454         "celldblclick" : true,
52455         /**
52456          * @event rowclick
52457          * Fires when a row is clicked
52458          * @param {Grid} this
52459          * @param {Number} rowIndex
52460          * @param {Roo.EventObject} e
52461          */
52462         "rowclick" : true,
52463         /**
52464          * @event rowdblclick
52465          * Fires when a row is double clicked
52466          * @param {Grid} this
52467          * @param {Number} rowIndex
52468          * @param {Roo.EventObject} e
52469          */
52470         "rowdblclick" : true,
52471         /**
52472          * @event headerclick
52473          * Fires when a header is clicked
52474          * @param {Grid} this
52475          * @param {Number} columnIndex
52476          * @param {Roo.EventObject} e
52477          */
52478         "headerclick" : true,
52479         /**
52480          * @event headerdblclick
52481          * Fires when a header cell is double clicked
52482          * @param {Grid} this
52483          * @param {Number} columnIndex
52484          * @param {Roo.EventObject} e
52485          */
52486         "headerdblclick" : true,
52487         /**
52488          * @event rowcontextmenu
52489          * Fires when a row is right clicked
52490          * @param {Grid} this
52491          * @param {Number} rowIndex
52492          * @param {Roo.EventObject} e
52493          */
52494         "rowcontextmenu" : true,
52495         /**
52496          * @event cellcontextmenu
52497          * Fires when a cell is right clicked
52498          * @param {Grid} this
52499          * @param {Number} rowIndex
52500          * @param {Number} cellIndex
52501          * @param {Roo.EventObject} e
52502          */
52503          "cellcontextmenu" : true,
52504         /**
52505          * @event headercontextmenu
52506          * Fires when a header is right clicked
52507          * @param {Grid} this
52508          * @param {Number} columnIndex
52509          * @param {Roo.EventObject} e
52510          */
52511         "headercontextmenu" : true,
52512         /**
52513          * @event bodyscroll
52514          * Fires when the body element is scrolled
52515          * @param {Number} scrollLeft
52516          * @param {Number} scrollTop
52517          */
52518         "bodyscroll" : true,
52519         /**
52520          * @event columnresize
52521          * Fires when the user resizes a column
52522          * @param {Number} columnIndex
52523          * @param {Number} newSize
52524          */
52525         "columnresize" : true,
52526         /**
52527          * @event columnmove
52528          * Fires when the user moves a column
52529          * @param {Number} oldIndex
52530          * @param {Number} newIndex
52531          */
52532         "columnmove" : true,
52533         /**
52534          * @event startdrag
52535          * Fires when row(s) start being dragged
52536          * @param {Grid} this
52537          * @param {Roo.GridDD} dd The drag drop object
52538          * @param {event} e The raw browser event
52539          */
52540         "startdrag" : true,
52541         /**
52542          * @event enddrag
52543          * Fires when a drag operation is complete
52544          * @param {Grid} this
52545          * @param {Roo.GridDD} dd The drag drop object
52546          * @param {event} e The raw browser event
52547          */
52548         "enddrag" : true,
52549         /**
52550          * @event dragdrop
52551          * Fires when dragged row(s) are dropped on a valid DD target
52552          * @param {Grid} this
52553          * @param {Roo.GridDD} dd The drag drop object
52554          * @param {String} targetId The target drag drop object
52555          * @param {event} e The raw browser event
52556          */
52557         "dragdrop" : true,
52558         /**
52559          * @event dragover
52560          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52561          * @param {Grid} this
52562          * @param {Roo.GridDD} dd The drag drop object
52563          * @param {String} targetId The target drag drop object
52564          * @param {event} e The raw browser event
52565          */
52566         "dragover" : true,
52567         /**
52568          * @event dragenter
52569          *  Fires when the dragged row(s) first cross another DD target while being dragged
52570          * @param {Grid} this
52571          * @param {Roo.GridDD} dd The drag drop object
52572          * @param {String} targetId The target drag drop object
52573          * @param {event} e The raw browser event
52574          */
52575         "dragenter" : true,
52576         /**
52577          * @event dragout
52578          * Fires when the dragged row(s) leave another DD target while being dragged
52579          * @param {Grid} this
52580          * @param {Roo.GridDD} dd The drag drop object
52581          * @param {String} targetId The target drag drop object
52582          * @param {event} e The raw browser event
52583          */
52584         "dragout" : true,
52585         /**
52586          * @event rowclass
52587          * Fires when a row is rendered, so you can change add a style to it.
52588          * @param {GridView} gridview   The grid view
52589          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52590          */
52591         'rowclass' : true,
52592
52593         /**
52594          * @event render
52595          * Fires when the grid is rendered
52596          * @param {Grid} grid
52597          */
52598         'render' : true
52599     });
52600
52601     Roo.grid.Grid.superclass.constructor.call(this);
52602 };
52603 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52604     
52605     /**
52606      * @cfg {String} ddGroup - drag drop group.
52607      */
52608
52609     /**
52610      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52611      */
52612     minColumnWidth : 25,
52613
52614     /**
52615      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52616      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52617      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52618      */
52619     autoSizeColumns : false,
52620
52621     /**
52622      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52623      */
52624     autoSizeHeaders : true,
52625
52626     /**
52627      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52628      */
52629     monitorWindowResize : true,
52630
52631     /**
52632      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52633      * rows measured to get a columns size. Default is 0 (all rows).
52634      */
52635     maxRowsToMeasure : 0,
52636
52637     /**
52638      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52639      */
52640     trackMouseOver : true,
52641
52642     /**
52643     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52644     */
52645     
52646     /**
52647     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52648     */
52649     enableDragDrop : false,
52650     
52651     /**
52652     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52653     */
52654     enableColumnMove : true,
52655     
52656     /**
52657     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52658     */
52659     enableColumnHide : true,
52660     
52661     /**
52662     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52663     */
52664     enableRowHeightSync : false,
52665     
52666     /**
52667     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52668     */
52669     stripeRows : true,
52670     
52671     /**
52672     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52673     */
52674     autoHeight : false,
52675
52676     /**
52677      * @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.
52678      */
52679     autoExpandColumn : false,
52680
52681     /**
52682     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52683     * Default is 50.
52684     */
52685     autoExpandMin : 50,
52686
52687     /**
52688     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52689     */
52690     autoExpandMax : 1000,
52691
52692     /**
52693     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52694     */
52695     view : null,
52696
52697     /**
52698     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52699     */
52700     loadMask : false,
52701     /**
52702     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52703     */
52704     dropTarget: false,
52705     
52706    
52707     
52708     // private
52709     rendered : false,
52710
52711     /**
52712     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52713     * of a fixed width. Default is false.
52714     */
52715     /**
52716     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52717     */
52718     /**
52719      * Called once after all setup has been completed and the grid is ready to be rendered.
52720      * @return {Roo.grid.Grid} this
52721      */
52722     render : function()
52723     {
52724         var c = this.container;
52725         // try to detect autoHeight/width mode
52726         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52727             this.autoHeight = true;
52728         }
52729         var view = this.getView();
52730         view.init(this);
52731
52732         c.on("click", this.onClick, this);
52733         c.on("dblclick", this.onDblClick, this);
52734         c.on("contextmenu", this.onContextMenu, this);
52735         c.on("keydown", this.onKeyDown, this);
52736         if (Roo.isTouch) {
52737             c.on("touchstart", this.onTouchStart, this);
52738         }
52739
52740         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52741
52742         this.getSelectionModel().init(this);
52743
52744         view.render();
52745
52746         if(this.loadMask){
52747             this.loadMask = new Roo.LoadMask(this.container,
52748                     Roo.apply({store:this.dataSource}, this.loadMask));
52749         }
52750         
52751         
52752         if (this.toolbar && this.toolbar.xtype) {
52753             this.toolbar.container = this.getView().getHeaderPanel(true);
52754             this.toolbar = new Roo.Toolbar(this.toolbar);
52755         }
52756         if (this.footer && this.footer.xtype) {
52757             this.footer.dataSource = this.getDataSource();
52758             this.footer.container = this.getView().getFooterPanel(true);
52759             this.footer = Roo.factory(this.footer, Roo);
52760         }
52761         if (this.dropTarget && this.dropTarget.xtype) {
52762             delete this.dropTarget.xtype;
52763             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52764         }
52765         
52766         
52767         this.rendered = true;
52768         this.fireEvent('render', this);
52769         return this;
52770     },
52771
52772         /**
52773          * Reconfigures the grid to use a different Store and Column Model.
52774          * The View will be bound to the new objects and refreshed.
52775          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52776          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52777          */
52778     reconfigure : function(dataSource, colModel){
52779         if(this.loadMask){
52780             this.loadMask.destroy();
52781             this.loadMask = new Roo.LoadMask(this.container,
52782                     Roo.apply({store:dataSource}, this.loadMask));
52783         }
52784         this.view.bind(dataSource, colModel);
52785         this.dataSource = dataSource;
52786         this.colModel = colModel;
52787         this.view.refresh(true);
52788     },
52789
52790     // private
52791     onKeyDown : function(e){
52792         this.fireEvent("keydown", e);
52793     },
52794
52795     /**
52796      * Destroy this grid.
52797      * @param {Boolean} removeEl True to remove the element
52798      */
52799     destroy : function(removeEl, keepListeners){
52800         if(this.loadMask){
52801             this.loadMask.destroy();
52802         }
52803         var c = this.container;
52804         c.removeAllListeners();
52805         this.view.destroy();
52806         this.colModel.purgeListeners();
52807         if(!keepListeners){
52808             this.purgeListeners();
52809         }
52810         c.update("");
52811         if(removeEl === true){
52812             c.remove();
52813         }
52814     },
52815
52816     // private
52817     processEvent : function(name, e){
52818         // does this fire select???
52819         //Roo.log('grid:processEvent '  + name);
52820         
52821         if (name != 'touchstart' ) {
52822             this.fireEvent(name, e);    
52823         }
52824         
52825         var t = e.getTarget();
52826         var v = this.view;
52827         var header = v.findHeaderIndex(t);
52828         if(header !== false){
52829             var ename = name == 'touchstart' ? 'click' : name;
52830              
52831             this.fireEvent("header" + ename, this, header, e);
52832         }else{
52833             var row = v.findRowIndex(t);
52834             var cell = v.findCellIndex(t);
52835             if (name == 'touchstart') {
52836                 // first touch is always a click.
52837                 // hopefull this happens after selection is updated.?
52838                 name = false;
52839                 
52840                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52841                     var cs = this.selModel.getSelectedCell();
52842                     if (row == cs[0] && cell == cs[1]){
52843                         name = 'dblclick';
52844                     }
52845                 }
52846                 if (typeof(this.selModel.getSelections) != 'undefined') {
52847                     var cs = this.selModel.getSelections();
52848                     var ds = this.dataSource;
52849                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52850                         name = 'dblclick';
52851                     }
52852                 }
52853                 if (!name) {
52854                     return;
52855                 }
52856             }
52857             
52858             
52859             if(row !== false){
52860                 this.fireEvent("row" + name, this, row, e);
52861                 if(cell !== false){
52862                     this.fireEvent("cell" + name, this, row, cell, e);
52863                 }
52864             }
52865         }
52866     },
52867
52868     // private
52869     onClick : function(e){
52870         this.processEvent("click", e);
52871     },
52872    // private
52873     onTouchStart : function(e){
52874         this.processEvent("touchstart", e);
52875     },
52876
52877     // private
52878     onContextMenu : function(e, t){
52879         this.processEvent("contextmenu", e);
52880     },
52881
52882     // private
52883     onDblClick : function(e){
52884         this.processEvent("dblclick", e);
52885     },
52886
52887     // private
52888     walkCells : function(row, col, step, fn, scope){
52889         var cm = this.colModel, clen = cm.getColumnCount();
52890         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52891         if(step < 0){
52892             if(col < 0){
52893                 row--;
52894                 first = false;
52895             }
52896             while(row >= 0){
52897                 if(!first){
52898                     col = clen-1;
52899                 }
52900                 first = false;
52901                 while(col >= 0){
52902                     if(fn.call(scope || this, row, col, cm) === true){
52903                         return [row, col];
52904                     }
52905                     col--;
52906                 }
52907                 row--;
52908             }
52909         } else {
52910             if(col >= clen){
52911                 row++;
52912                 first = false;
52913             }
52914             while(row < rlen){
52915                 if(!first){
52916                     col = 0;
52917                 }
52918                 first = false;
52919                 while(col < clen){
52920                     if(fn.call(scope || this, row, col, cm) === true){
52921                         return [row, col];
52922                     }
52923                     col++;
52924                 }
52925                 row++;
52926             }
52927         }
52928         return null;
52929     },
52930
52931     // private
52932     getSelections : function(){
52933         return this.selModel.getSelections();
52934     },
52935
52936     /**
52937      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52938      * but if manual update is required this method will initiate it.
52939      */
52940     autoSize : function(){
52941         if(this.rendered){
52942             this.view.layout();
52943             if(this.view.adjustForScroll){
52944                 this.view.adjustForScroll();
52945             }
52946         }
52947     },
52948
52949     /**
52950      * Returns the grid's underlying element.
52951      * @return {Element} The element
52952      */
52953     getGridEl : function(){
52954         return this.container;
52955     },
52956
52957     // private for compatibility, overridden by editor grid
52958     stopEditing : function(){},
52959
52960     /**
52961      * Returns the grid's SelectionModel.
52962      * @return {SelectionModel}
52963      */
52964     getSelectionModel : function(){
52965         if(!this.selModel){
52966             this.selModel = new Roo.grid.RowSelectionModel();
52967         }
52968         return this.selModel;
52969     },
52970
52971     /**
52972      * Returns the grid's DataSource.
52973      * @return {DataSource}
52974      */
52975     getDataSource : function(){
52976         return this.dataSource;
52977     },
52978
52979     /**
52980      * Returns the grid's ColumnModel.
52981      * @return {ColumnModel}
52982      */
52983     getColumnModel : function(){
52984         return this.colModel;
52985     },
52986
52987     /**
52988      * Returns the grid's GridView object.
52989      * @return {GridView}
52990      */
52991     getView : function(){
52992         if(!this.view){
52993             this.view = new Roo.grid.GridView(this.viewConfig);
52994         }
52995         return this.view;
52996     },
52997     /**
52998      * Called to get grid's drag proxy text, by default returns this.ddText.
52999      * @return {String}
53000      */
53001     getDragDropText : function(){
53002         var count = this.selModel.getCount();
53003         return String.format(this.ddText, count, count == 1 ? '' : 's');
53004     }
53005 });
53006 /**
53007  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53008  * %0 is replaced with the number of selected rows.
53009  * @type String
53010  */
53011 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53012  * Based on:
53013  * Ext JS Library 1.1.1
53014  * Copyright(c) 2006-2007, Ext JS, LLC.
53015  *
53016  * Originally Released Under LGPL - original licence link has changed is not relivant.
53017  *
53018  * Fork - LGPL
53019  * <script type="text/javascript">
53020  */
53021  
53022 Roo.grid.AbstractGridView = function(){
53023         this.grid = null;
53024         
53025         this.events = {
53026             "beforerowremoved" : true,
53027             "beforerowsinserted" : true,
53028             "beforerefresh" : true,
53029             "rowremoved" : true,
53030             "rowsinserted" : true,
53031             "rowupdated" : true,
53032             "refresh" : true
53033         };
53034     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53035 };
53036
53037 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53038     rowClass : "x-grid-row",
53039     cellClass : "x-grid-cell",
53040     tdClass : "x-grid-td",
53041     hdClass : "x-grid-hd",
53042     splitClass : "x-grid-hd-split",
53043     
53044     init: function(grid){
53045         this.grid = grid;
53046                 var cid = this.grid.getGridEl().id;
53047         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53048         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53049         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53050         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53051         },
53052         
53053     getColumnRenderers : function(){
53054         var renderers = [];
53055         var cm = this.grid.colModel;
53056         var colCount = cm.getColumnCount();
53057         for(var i = 0; i < colCount; i++){
53058             renderers[i] = cm.getRenderer(i);
53059         }
53060         return renderers;
53061     },
53062     
53063     getColumnIds : function(){
53064         var ids = [];
53065         var cm = this.grid.colModel;
53066         var colCount = cm.getColumnCount();
53067         for(var i = 0; i < colCount; i++){
53068             ids[i] = cm.getColumnId(i);
53069         }
53070         return ids;
53071     },
53072     
53073     getDataIndexes : function(){
53074         if(!this.indexMap){
53075             this.indexMap = this.buildIndexMap();
53076         }
53077         return this.indexMap.colToData;
53078     },
53079     
53080     getColumnIndexByDataIndex : function(dataIndex){
53081         if(!this.indexMap){
53082             this.indexMap = this.buildIndexMap();
53083         }
53084         return this.indexMap.dataToCol[dataIndex];
53085     },
53086     
53087     /**
53088      * Set a css style for a column dynamically. 
53089      * @param {Number} colIndex The index of the column
53090      * @param {String} name The css property name
53091      * @param {String} value The css value
53092      */
53093     setCSSStyle : function(colIndex, name, value){
53094         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53095         Roo.util.CSS.updateRule(selector, name, value);
53096     },
53097     
53098     generateRules : function(cm){
53099         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53100         Roo.util.CSS.removeStyleSheet(rulesId);
53101         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53102             var cid = cm.getColumnId(i);
53103             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53104                          this.tdSelector, cid, " {\n}\n",
53105                          this.hdSelector, cid, " {\n}\n",
53106                          this.splitSelector, cid, " {\n}\n");
53107         }
53108         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53109     }
53110 });/*
53111  * Based on:
53112  * Ext JS Library 1.1.1
53113  * Copyright(c) 2006-2007, Ext JS, LLC.
53114  *
53115  * Originally Released Under LGPL - original licence link has changed is not relivant.
53116  *
53117  * Fork - LGPL
53118  * <script type="text/javascript">
53119  */
53120
53121 // private
53122 // This is a support class used internally by the Grid components
53123 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53124     this.grid = grid;
53125     this.view = grid.getView();
53126     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53127     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53128     if(hd2){
53129         this.setHandleElId(Roo.id(hd));
53130         this.setOuterHandleElId(Roo.id(hd2));
53131     }
53132     this.scroll = false;
53133 };
53134 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53135     maxDragWidth: 120,
53136     getDragData : function(e){
53137         var t = Roo.lib.Event.getTarget(e);
53138         var h = this.view.findHeaderCell(t);
53139         if(h){
53140             return {ddel: h.firstChild, header:h};
53141         }
53142         return false;
53143     },
53144
53145     onInitDrag : function(e){
53146         this.view.headersDisabled = true;
53147         var clone = this.dragData.ddel.cloneNode(true);
53148         clone.id = Roo.id();
53149         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53150         this.proxy.update(clone);
53151         return true;
53152     },
53153
53154     afterValidDrop : function(){
53155         var v = this.view;
53156         setTimeout(function(){
53157             v.headersDisabled = false;
53158         }, 50);
53159     },
53160
53161     afterInvalidDrop : function(){
53162         var v = this.view;
53163         setTimeout(function(){
53164             v.headersDisabled = false;
53165         }, 50);
53166     }
53167 });
53168 /*
53169  * Based on:
53170  * Ext JS Library 1.1.1
53171  * Copyright(c) 2006-2007, Ext JS, LLC.
53172  *
53173  * Originally Released Under LGPL - original licence link has changed is not relivant.
53174  *
53175  * Fork - LGPL
53176  * <script type="text/javascript">
53177  */
53178 // private
53179 // This is a support class used internally by the Grid components
53180 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53181     this.grid = grid;
53182     this.view = grid.getView();
53183     // split the proxies so they don't interfere with mouse events
53184     this.proxyTop = Roo.DomHelper.append(document.body, {
53185         cls:"col-move-top", html:"&#160;"
53186     }, true);
53187     this.proxyBottom = Roo.DomHelper.append(document.body, {
53188         cls:"col-move-bottom", html:"&#160;"
53189     }, true);
53190     this.proxyTop.hide = this.proxyBottom.hide = function(){
53191         this.setLeftTop(-100,-100);
53192         this.setStyle("visibility", "hidden");
53193     };
53194     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53195     // temporarily disabled
53196     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53197     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53198 };
53199 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53200     proxyOffsets : [-4, -9],
53201     fly: Roo.Element.fly,
53202
53203     getTargetFromEvent : function(e){
53204         var t = Roo.lib.Event.getTarget(e);
53205         var cindex = this.view.findCellIndex(t);
53206         if(cindex !== false){
53207             return this.view.getHeaderCell(cindex);
53208         }
53209         return null;
53210     },
53211
53212     nextVisible : function(h){
53213         var v = this.view, cm = this.grid.colModel;
53214         h = h.nextSibling;
53215         while(h){
53216             if(!cm.isHidden(v.getCellIndex(h))){
53217                 return h;
53218             }
53219             h = h.nextSibling;
53220         }
53221         return null;
53222     },
53223
53224     prevVisible : function(h){
53225         var v = this.view, cm = this.grid.colModel;
53226         h = h.prevSibling;
53227         while(h){
53228             if(!cm.isHidden(v.getCellIndex(h))){
53229                 return h;
53230             }
53231             h = h.prevSibling;
53232         }
53233         return null;
53234     },
53235
53236     positionIndicator : function(h, n, e){
53237         var x = Roo.lib.Event.getPageX(e);
53238         var r = Roo.lib.Dom.getRegion(n.firstChild);
53239         var px, pt, py = r.top + this.proxyOffsets[1];
53240         if((r.right - x) <= (r.right-r.left)/2){
53241             px = r.right+this.view.borderWidth;
53242             pt = "after";
53243         }else{
53244             px = r.left;
53245             pt = "before";
53246         }
53247         var oldIndex = this.view.getCellIndex(h);
53248         var newIndex = this.view.getCellIndex(n);
53249
53250         if(this.grid.colModel.isFixed(newIndex)){
53251             return false;
53252         }
53253
53254         var locked = this.grid.colModel.isLocked(newIndex);
53255
53256         if(pt == "after"){
53257             newIndex++;
53258         }
53259         if(oldIndex < newIndex){
53260             newIndex--;
53261         }
53262         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53263             return false;
53264         }
53265         px +=  this.proxyOffsets[0];
53266         this.proxyTop.setLeftTop(px, py);
53267         this.proxyTop.show();
53268         if(!this.bottomOffset){
53269             this.bottomOffset = this.view.mainHd.getHeight();
53270         }
53271         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53272         this.proxyBottom.show();
53273         return pt;
53274     },
53275
53276     onNodeEnter : function(n, dd, e, data){
53277         if(data.header != n){
53278             this.positionIndicator(data.header, n, e);
53279         }
53280     },
53281
53282     onNodeOver : function(n, dd, e, data){
53283         var result = false;
53284         if(data.header != n){
53285             result = this.positionIndicator(data.header, n, e);
53286         }
53287         if(!result){
53288             this.proxyTop.hide();
53289             this.proxyBottom.hide();
53290         }
53291         return result ? this.dropAllowed : this.dropNotAllowed;
53292     },
53293
53294     onNodeOut : function(n, dd, e, data){
53295         this.proxyTop.hide();
53296         this.proxyBottom.hide();
53297     },
53298
53299     onNodeDrop : function(n, dd, e, data){
53300         var h = data.header;
53301         if(h != n){
53302             var cm = this.grid.colModel;
53303             var x = Roo.lib.Event.getPageX(e);
53304             var r = Roo.lib.Dom.getRegion(n.firstChild);
53305             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53306             var oldIndex = this.view.getCellIndex(h);
53307             var newIndex = this.view.getCellIndex(n);
53308             var locked = cm.isLocked(newIndex);
53309             if(pt == "after"){
53310                 newIndex++;
53311             }
53312             if(oldIndex < newIndex){
53313                 newIndex--;
53314             }
53315             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53316                 return false;
53317             }
53318             cm.setLocked(oldIndex, locked, true);
53319             cm.moveColumn(oldIndex, newIndex);
53320             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53321             return true;
53322         }
53323         return false;
53324     }
53325 });
53326 /*
53327  * Based on:
53328  * Ext JS Library 1.1.1
53329  * Copyright(c) 2006-2007, Ext JS, LLC.
53330  *
53331  * Originally Released Under LGPL - original licence link has changed is not relivant.
53332  *
53333  * Fork - LGPL
53334  * <script type="text/javascript">
53335  */
53336   
53337 /**
53338  * @class Roo.grid.GridView
53339  * @extends Roo.util.Observable
53340  *
53341  * @constructor
53342  * @param {Object} config
53343  */
53344 Roo.grid.GridView = function(config){
53345     Roo.grid.GridView.superclass.constructor.call(this);
53346     this.el = null;
53347
53348     Roo.apply(this, config);
53349 };
53350
53351 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53352
53353     unselectable :  'unselectable="on"',
53354     unselectableCls :  'x-unselectable',
53355     
53356     
53357     rowClass : "x-grid-row",
53358
53359     cellClass : "x-grid-col",
53360
53361     tdClass : "x-grid-td",
53362
53363     hdClass : "x-grid-hd",
53364
53365     splitClass : "x-grid-split",
53366
53367     sortClasses : ["sort-asc", "sort-desc"],
53368
53369     enableMoveAnim : false,
53370
53371     hlColor: "C3DAF9",
53372
53373     dh : Roo.DomHelper,
53374
53375     fly : Roo.Element.fly,
53376
53377     css : Roo.util.CSS,
53378
53379     borderWidth: 1,
53380
53381     splitOffset: 3,
53382
53383     scrollIncrement : 22,
53384
53385     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53386
53387     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53388
53389     bind : function(ds, cm){
53390         if(this.ds){
53391             this.ds.un("load", this.onLoad, this);
53392             this.ds.un("datachanged", this.onDataChange, this);
53393             this.ds.un("add", this.onAdd, this);
53394             this.ds.un("remove", this.onRemove, this);
53395             this.ds.un("update", this.onUpdate, this);
53396             this.ds.un("clear", this.onClear, this);
53397         }
53398         if(ds){
53399             ds.on("load", this.onLoad, this);
53400             ds.on("datachanged", this.onDataChange, this);
53401             ds.on("add", this.onAdd, this);
53402             ds.on("remove", this.onRemove, this);
53403             ds.on("update", this.onUpdate, this);
53404             ds.on("clear", this.onClear, this);
53405         }
53406         this.ds = ds;
53407
53408         if(this.cm){
53409             this.cm.un("widthchange", this.onColWidthChange, this);
53410             this.cm.un("headerchange", this.onHeaderChange, this);
53411             this.cm.un("hiddenchange", this.onHiddenChange, this);
53412             this.cm.un("columnmoved", this.onColumnMove, this);
53413             this.cm.un("columnlockchange", this.onColumnLock, this);
53414         }
53415         if(cm){
53416             this.generateRules(cm);
53417             cm.on("widthchange", this.onColWidthChange, this);
53418             cm.on("headerchange", this.onHeaderChange, this);
53419             cm.on("hiddenchange", this.onHiddenChange, this);
53420             cm.on("columnmoved", this.onColumnMove, this);
53421             cm.on("columnlockchange", this.onColumnLock, this);
53422         }
53423         this.cm = cm;
53424     },
53425
53426     init: function(grid){
53427         Roo.grid.GridView.superclass.init.call(this, grid);
53428
53429         this.bind(grid.dataSource, grid.colModel);
53430
53431         grid.on("headerclick", this.handleHeaderClick, this);
53432
53433         if(grid.trackMouseOver){
53434             grid.on("mouseover", this.onRowOver, this);
53435             grid.on("mouseout", this.onRowOut, this);
53436         }
53437         grid.cancelTextSelection = function(){};
53438         this.gridId = grid.id;
53439
53440         var tpls = this.templates || {};
53441
53442         if(!tpls.master){
53443             tpls.master = new Roo.Template(
53444                '<div class="x-grid" hidefocus="true">',
53445                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53446                   '<div class="x-grid-topbar"></div>',
53447                   '<div class="x-grid-scroller"><div></div></div>',
53448                   '<div class="x-grid-locked">',
53449                       '<div class="x-grid-header">{lockedHeader}</div>',
53450                       '<div class="x-grid-body">{lockedBody}</div>',
53451                   "</div>",
53452                   '<div class="x-grid-viewport">',
53453                       '<div class="x-grid-header">{header}</div>',
53454                       '<div class="x-grid-body">{body}</div>',
53455                   "</div>",
53456                   '<div class="x-grid-bottombar"></div>',
53457                  
53458                   '<div class="x-grid-resize-proxy">&#160;</div>',
53459                "</div>"
53460             );
53461             tpls.master.disableformats = true;
53462         }
53463
53464         if(!tpls.header){
53465             tpls.header = new Roo.Template(
53466                '<table border="0" cellspacing="0" cellpadding="0">',
53467                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53468                "</table>{splits}"
53469             );
53470             tpls.header.disableformats = true;
53471         }
53472         tpls.header.compile();
53473
53474         if(!tpls.hcell){
53475             tpls.hcell = new Roo.Template(
53476                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53477                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53478                 "</div></td>"
53479              );
53480              tpls.hcell.disableFormats = true;
53481         }
53482         tpls.hcell.compile();
53483
53484         if(!tpls.hsplit){
53485             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53486                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53487             tpls.hsplit.disableFormats = true;
53488         }
53489         tpls.hsplit.compile();
53490
53491         if(!tpls.body){
53492             tpls.body = new Roo.Template(
53493                '<table border="0" cellspacing="0" cellpadding="0">',
53494                "<tbody>{rows}</tbody>",
53495                "</table>"
53496             );
53497             tpls.body.disableFormats = true;
53498         }
53499         tpls.body.compile();
53500
53501         if(!tpls.row){
53502             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53503             tpls.row.disableFormats = true;
53504         }
53505         tpls.row.compile();
53506
53507         if(!tpls.cell){
53508             tpls.cell = new Roo.Template(
53509                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53510                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53511                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53512                 "</td>"
53513             );
53514             tpls.cell.disableFormats = true;
53515         }
53516         tpls.cell.compile();
53517
53518         this.templates = tpls;
53519     },
53520
53521     // remap these for backwards compat
53522     onColWidthChange : function(){
53523         this.updateColumns.apply(this, arguments);
53524     },
53525     onHeaderChange : function(){
53526         this.updateHeaders.apply(this, arguments);
53527     }, 
53528     onHiddenChange : function(){
53529         this.handleHiddenChange.apply(this, arguments);
53530     },
53531     onColumnMove : function(){
53532         this.handleColumnMove.apply(this, arguments);
53533     },
53534     onColumnLock : function(){
53535         this.handleLockChange.apply(this, arguments);
53536     },
53537
53538     onDataChange : function(){
53539         this.refresh();
53540         this.updateHeaderSortState();
53541     },
53542
53543     onClear : function(){
53544         this.refresh();
53545     },
53546
53547     onUpdate : function(ds, record){
53548         this.refreshRow(record);
53549     },
53550
53551     refreshRow : function(record){
53552         var ds = this.ds, index;
53553         if(typeof record == 'number'){
53554             index = record;
53555             record = ds.getAt(index);
53556         }else{
53557             index = ds.indexOf(record);
53558         }
53559         this.insertRows(ds, index, index, true);
53560         this.onRemove(ds, record, index+1, true);
53561         this.syncRowHeights(index, index);
53562         this.layout();
53563         this.fireEvent("rowupdated", this, index, record);
53564     },
53565
53566     onAdd : function(ds, records, index){
53567         this.insertRows(ds, index, index + (records.length-1));
53568     },
53569
53570     onRemove : function(ds, record, index, isUpdate){
53571         if(isUpdate !== true){
53572             this.fireEvent("beforerowremoved", this, index, record);
53573         }
53574         var bt = this.getBodyTable(), lt = this.getLockedTable();
53575         if(bt.rows[index]){
53576             bt.firstChild.removeChild(bt.rows[index]);
53577         }
53578         if(lt.rows[index]){
53579             lt.firstChild.removeChild(lt.rows[index]);
53580         }
53581         if(isUpdate !== true){
53582             this.stripeRows(index);
53583             this.syncRowHeights(index, index);
53584             this.layout();
53585             this.fireEvent("rowremoved", this, index, record);
53586         }
53587     },
53588
53589     onLoad : function(){
53590         this.scrollToTop();
53591     },
53592
53593     /**
53594      * Scrolls the grid to the top
53595      */
53596     scrollToTop : function(){
53597         if(this.scroller){
53598             this.scroller.dom.scrollTop = 0;
53599             this.syncScroll();
53600         }
53601     },
53602
53603     /**
53604      * Gets a panel in the header of the grid that can be used for toolbars etc.
53605      * After modifying the contents of this panel a call to grid.autoSize() may be
53606      * required to register any changes in size.
53607      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53608      * @return Roo.Element
53609      */
53610     getHeaderPanel : function(doShow){
53611         if(doShow){
53612             this.headerPanel.show();
53613         }
53614         return this.headerPanel;
53615     },
53616
53617     /**
53618      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53619      * After modifying the contents of this panel a call to grid.autoSize() may be
53620      * required to register any changes in size.
53621      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53622      * @return Roo.Element
53623      */
53624     getFooterPanel : function(doShow){
53625         if(doShow){
53626             this.footerPanel.show();
53627         }
53628         return this.footerPanel;
53629     },
53630
53631     initElements : function(){
53632         var E = Roo.Element;
53633         var el = this.grid.getGridEl().dom.firstChild;
53634         var cs = el.childNodes;
53635
53636         this.el = new E(el);
53637         
53638          this.focusEl = new E(el.firstChild);
53639         this.focusEl.swallowEvent("click", true);
53640         
53641         this.headerPanel = new E(cs[1]);
53642         this.headerPanel.enableDisplayMode("block");
53643
53644         this.scroller = new E(cs[2]);
53645         this.scrollSizer = new E(this.scroller.dom.firstChild);
53646
53647         this.lockedWrap = new E(cs[3]);
53648         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53649         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53650
53651         this.mainWrap = new E(cs[4]);
53652         this.mainHd = new E(this.mainWrap.dom.firstChild);
53653         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53654
53655         this.footerPanel = new E(cs[5]);
53656         this.footerPanel.enableDisplayMode("block");
53657
53658         this.resizeProxy = new E(cs[6]);
53659
53660         this.headerSelector = String.format(
53661            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53662            this.lockedHd.id, this.mainHd.id
53663         );
53664
53665         this.splitterSelector = String.format(
53666            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53667            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53668         );
53669     },
53670     idToCssName : function(s)
53671     {
53672         return s.replace(/[^a-z0-9]+/ig, '-');
53673     },
53674
53675     getHeaderCell : function(index){
53676         return Roo.DomQuery.select(this.headerSelector)[index];
53677     },
53678
53679     getHeaderCellMeasure : function(index){
53680         return this.getHeaderCell(index).firstChild;
53681     },
53682
53683     getHeaderCellText : function(index){
53684         return this.getHeaderCell(index).firstChild.firstChild;
53685     },
53686
53687     getLockedTable : function(){
53688         return this.lockedBody.dom.firstChild;
53689     },
53690
53691     getBodyTable : function(){
53692         return this.mainBody.dom.firstChild;
53693     },
53694
53695     getLockedRow : function(index){
53696         return this.getLockedTable().rows[index];
53697     },
53698
53699     getRow : function(index){
53700         return this.getBodyTable().rows[index];
53701     },
53702
53703     getRowComposite : function(index){
53704         if(!this.rowEl){
53705             this.rowEl = new Roo.CompositeElementLite();
53706         }
53707         var els = [], lrow, mrow;
53708         if(lrow = this.getLockedRow(index)){
53709             els.push(lrow);
53710         }
53711         if(mrow = this.getRow(index)){
53712             els.push(mrow);
53713         }
53714         this.rowEl.elements = els;
53715         return this.rowEl;
53716     },
53717     /**
53718      * Gets the 'td' of the cell
53719      * 
53720      * @param {Integer} rowIndex row to select
53721      * @param {Integer} colIndex column to select
53722      * 
53723      * @return {Object} 
53724      */
53725     getCell : function(rowIndex, colIndex){
53726         var locked = this.cm.getLockedCount();
53727         var source;
53728         if(colIndex < locked){
53729             source = this.lockedBody.dom.firstChild;
53730         }else{
53731             source = this.mainBody.dom.firstChild;
53732             colIndex -= locked;
53733         }
53734         return source.rows[rowIndex].childNodes[colIndex];
53735     },
53736
53737     getCellText : function(rowIndex, colIndex){
53738         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53739     },
53740
53741     getCellBox : function(cell){
53742         var b = this.fly(cell).getBox();
53743         if(Roo.isOpera){ // opera fails to report the Y
53744             b.y = cell.offsetTop + this.mainBody.getY();
53745         }
53746         return b;
53747     },
53748
53749     getCellIndex : function(cell){
53750         var id = String(cell.className).match(this.cellRE);
53751         if(id){
53752             return parseInt(id[1], 10);
53753         }
53754         return 0;
53755     },
53756
53757     findHeaderIndex : function(n){
53758         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53759         return r ? this.getCellIndex(r) : false;
53760     },
53761
53762     findHeaderCell : function(n){
53763         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53764         return r ? r : false;
53765     },
53766
53767     findRowIndex : function(n){
53768         if(!n){
53769             return false;
53770         }
53771         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53772         return r ? r.rowIndex : false;
53773     },
53774
53775     findCellIndex : function(node){
53776         var stop = this.el.dom;
53777         while(node && node != stop){
53778             if(this.findRE.test(node.className)){
53779                 return this.getCellIndex(node);
53780             }
53781             node = node.parentNode;
53782         }
53783         return false;
53784     },
53785
53786     getColumnId : function(index){
53787         return this.cm.getColumnId(index);
53788     },
53789
53790     getSplitters : function()
53791     {
53792         if(this.splitterSelector){
53793            return Roo.DomQuery.select(this.splitterSelector);
53794         }else{
53795             return null;
53796       }
53797     },
53798
53799     getSplitter : function(index){
53800         return this.getSplitters()[index];
53801     },
53802
53803     onRowOver : function(e, t){
53804         var row;
53805         if((row = this.findRowIndex(t)) !== false){
53806             this.getRowComposite(row).addClass("x-grid-row-over");
53807         }
53808     },
53809
53810     onRowOut : function(e, t){
53811         var row;
53812         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53813             this.getRowComposite(row).removeClass("x-grid-row-over");
53814         }
53815     },
53816
53817     renderHeaders : function(){
53818         var cm = this.cm;
53819         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53820         var cb = [], lb = [], sb = [], lsb = [], p = {};
53821         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53822             p.cellId = "x-grid-hd-0-" + i;
53823             p.splitId = "x-grid-csplit-0-" + i;
53824             p.id = cm.getColumnId(i);
53825             p.title = cm.getColumnTooltip(i) || "";
53826             p.value = cm.getColumnHeader(i) || "";
53827             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53828             if(!cm.isLocked(i)){
53829                 cb[cb.length] = ct.apply(p);
53830                 sb[sb.length] = st.apply(p);
53831             }else{
53832                 lb[lb.length] = ct.apply(p);
53833                 lsb[lsb.length] = st.apply(p);
53834             }
53835         }
53836         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53837                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53838     },
53839
53840     updateHeaders : function(){
53841         var html = this.renderHeaders();
53842         this.lockedHd.update(html[0]);
53843         this.mainHd.update(html[1]);
53844     },
53845
53846     /**
53847      * Focuses the specified row.
53848      * @param {Number} row The row index
53849      */
53850     focusRow : function(row)
53851     {
53852         //Roo.log('GridView.focusRow');
53853         var x = this.scroller.dom.scrollLeft;
53854         this.focusCell(row, 0, false);
53855         this.scroller.dom.scrollLeft = x;
53856     },
53857
53858     /**
53859      * Focuses the specified cell.
53860      * @param {Number} row The row index
53861      * @param {Number} col The column index
53862      * @param {Boolean} hscroll false to disable horizontal scrolling
53863      */
53864     focusCell : function(row, col, hscroll)
53865     {
53866         //Roo.log('GridView.focusCell');
53867         var el = this.ensureVisible(row, col, hscroll);
53868         this.focusEl.alignTo(el, "tl-tl");
53869         if(Roo.isGecko){
53870             this.focusEl.focus();
53871         }else{
53872             this.focusEl.focus.defer(1, this.focusEl);
53873         }
53874     },
53875
53876     /**
53877      * Scrolls the specified cell into view
53878      * @param {Number} row The row index
53879      * @param {Number} col The column index
53880      * @param {Boolean} hscroll false to disable horizontal scrolling
53881      */
53882     ensureVisible : function(row, col, hscroll)
53883     {
53884         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53885         //return null; //disable for testing.
53886         if(typeof row != "number"){
53887             row = row.rowIndex;
53888         }
53889         if(row < 0 && row >= this.ds.getCount()){
53890             return  null;
53891         }
53892         col = (col !== undefined ? col : 0);
53893         var cm = this.grid.colModel;
53894         while(cm.isHidden(col)){
53895             col++;
53896         }
53897
53898         var el = this.getCell(row, col);
53899         if(!el){
53900             return null;
53901         }
53902         var c = this.scroller.dom;
53903
53904         var ctop = parseInt(el.offsetTop, 10);
53905         var cleft = parseInt(el.offsetLeft, 10);
53906         var cbot = ctop + el.offsetHeight;
53907         var cright = cleft + el.offsetWidth;
53908         
53909         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53910         var stop = parseInt(c.scrollTop, 10);
53911         var sleft = parseInt(c.scrollLeft, 10);
53912         var sbot = stop + ch;
53913         var sright = sleft + c.clientWidth;
53914         /*
53915         Roo.log('GridView.ensureVisible:' +
53916                 ' ctop:' + ctop +
53917                 ' c.clientHeight:' + c.clientHeight +
53918                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53919                 ' stop:' + stop +
53920                 ' cbot:' + cbot +
53921                 ' sbot:' + sbot +
53922                 ' ch:' + ch  
53923                 );
53924         */
53925         if(ctop < stop){
53926              c.scrollTop = ctop;
53927             //Roo.log("set scrolltop to ctop DISABLE?");
53928         }else if(cbot > sbot){
53929             //Roo.log("set scrolltop to cbot-ch");
53930             c.scrollTop = cbot-ch;
53931         }
53932         
53933         if(hscroll !== false){
53934             if(cleft < sleft){
53935                 c.scrollLeft = cleft;
53936             }else if(cright > sright){
53937                 c.scrollLeft = cright-c.clientWidth;
53938             }
53939         }
53940          
53941         return el;
53942     },
53943
53944     updateColumns : function(){
53945         this.grid.stopEditing();
53946         var cm = this.grid.colModel, colIds = this.getColumnIds();
53947         //var totalWidth = cm.getTotalWidth();
53948         var pos = 0;
53949         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53950             //if(cm.isHidden(i)) continue;
53951             var w = cm.getColumnWidth(i);
53952             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53953             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53954         }
53955         this.updateSplitters();
53956     },
53957
53958     generateRules : function(cm){
53959         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53960         Roo.util.CSS.removeStyleSheet(rulesId);
53961         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53962             var cid = cm.getColumnId(i);
53963             var align = '';
53964             if(cm.config[i].align){
53965                 align = 'text-align:'+cm.config[i].align+';';
53966             }
53967             var hidden = '';
53968             if(cm.isHidden(i)){
53969                 hidden = 'display:none;';
53970             }
53971             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53972             ruleBuf.push(
53973                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53974                     this.hdSelector, cid, " {\n", align, width, "}\n",
53975                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53976                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53977         }
53978         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53979     },
53980
53981     updateSplitters : function(){
53982         var cm = this.cm, s = this.getSplitters();
53983         if(s){ // splitters not created yet
53984             var pos = 0, locked = true;
53985             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53986                 if(cm.isHidden(i)) continue;
53987                 var w = cm.getColumnWidth(i); // make sure it's a number
53988                 if(!cm.isLocked(i) && locked){
53989                     pos = 0;
53990                     locked = false;
53991                 }
53992                 pos += w;
53993                 s[i].style.left = (pos-this.splitOffset) + "px";
53994             }
53995         }
53996     },
53997
53998     handleHiddenChange : function(colModel, colIndex, hidden){
53999         if(hidden){
54000             this.hideColumn(colIndex);
54001         }else{
54002             this.unhideColumn(colIndex);
54003         }
54004     },
54005
54006     hideColumn : function(colIndex){
54007         var cid = this.getColumnId(colIndex);
54008         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54009         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54010         if(Roo.isSafari){
54011             this.updateHeaders();
54012         }
54013         this.updateSplitters();
54014         this.layout();
54015     },
54016
54017     unhideColumn : function(colIndex){
54018         var cid = this.getColumnId(colIndex);
54019         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54020         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54021
54022         if(Roo.isSafari){
54023             this.updateHeaders();
54024         }
54025         this.updateSplitters();
54026         this.layout();
54027     },
54028
54029     insertRows : function(dm, firstRow, lastRow, isUpdate){
54030         if(firstRow == 0 && lastRow == dm.getCount()-1){
54031             this.refresh();
54032         }else{
54033             if(!isUpdate){
54034                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54035             }
54036             var s = this.getScrollState();
54037             var markup = this.renderRows(firstRow, lastRow);
54038             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54039             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54040             this.restoreScroll(s);
54041             if(!isUpdate){
54042                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54043                 this.syncRowHeights(firstRow, lastRow);
54044                 this.stripeRows(firstRow);
54045                 this.layout();
54046             }
54047         }
54048     },
54049
54050     bufferRows : function(markup, target, index){
54051         var before = null, trows = target.rows, tbody = target.tBodies[0];
54052         if(index < trows.length){
54053             before = trows[index];
54054         }
54055         var b = document.createElement("div");
54056         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54057         var rows = b.firstChild.rows;
54058         for(var i = 0, len = rows.length; i < len; i++){
54059             if(before){
54060                 tbody.insertBefore(rows[0], before);
54061             }else{
54062                 tbody.appendChild(rows[0]);
54063             }
54064         }
54065         b.innerHTML = "";
54066         b = null;
54067     },
54068
54069     deleteRows : function(dm, firstRow, lastRow){
54070         if(dm.getRowCount()<1){
54071             this.fireEvent("beforerefresh", this);
54072             this.mainBody.update("");
54073             this.lockedBody.update("");
54074             this.fireEvent("refresh", this);
54075         }else{
54076             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54077             var bt = this.getBodyTable();
54078             var tbody = bt.firstChild;
54079             var rows = bt.rows;
54080             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54081                 tbody.removeChild(rows[firstRow]);
54082             }
54083             this.stripeRows(firstRow);
54084             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54085         }
54086     },
54087
54088     updateRows : function(dataSource, firstRow, lastRow){
54089         var s = this.getScrollState();
54090         this.refresh();
54091         this.restoreScroll(s);
54092     },
54093
54094     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54095         if(!noRefresh){
54096            this.refresh();
54097         }
54098         this.updateHeaderSortState();
54099     },
54100
54101     getScrollState : function(){
54102         
54103         var sb = this.scroller.dom;
54104         return {left: sb.scrollLeft, top: sb.scrollTop};
54105     },
54106
54107     stripeRows : function(startRow){
54108         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54109             return;
54110         }
54111         startRow = startRow || 0;
54112         var rows = this.getBodyTable().rows;
54113         var lrows = this.getLockedTable().rows;
54114         var cls = ' x-grid-row-alt ';
54115         for(var i = startRow, len = rows.length; i < len; i++){
54116             var row = rows[i], lrow = lrows[i];
54117             var isAlt = ((i+1) % 2 == 0);
54118             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54119             if(isAlt == hasAlt){
54120                 continue;
54121             }
54122             if(isAlt){
54123                 row.className += " x-grid-row-alt";
54124             }else{
54125                 row.className = row.className.replace("x-grid-row-alt", "");
54126             }
54127             if(lrow){
54128                 lrow.className = row.className;
54129             }
54130         }
54131     },
54132
54133     restoreScroll : function(state){
54134         //Roo.log('GridView.restoreScroll');
54135         var sb = this.scroller.dom;
54136         sb.scrollLeft = state.left;
54137         sb.scrollTop = state.top;
54138         this.syncScroll();
54139     },
54140
54141     syncScroll : function(){
54142         //Roo.log('GridView.syncScroll');
54143         var sb = this.scroller.dom;
54144         var sh = this.mainHd.dom;
54145         var bs = this.mainBody.dom;
54146         var lv = this.lockedBody.dom;
54147         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54148         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54149     },
54150
54151     handleScroll : function(e){
54152         this.syncScroll();
54153         var sb = this.scroller.dom;
54154         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54155         e.stopEvent();
54156     },
54157
54158     handleWheel : function(e){
54159         var d = e.getWheelDelta();
54160         this.scroller.dom.scrollTop -= d*22;
54161         // set this here to prevent jumpy scrolling on large tables
54162         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54163         e.stopEvent();
54164     },
54165
54166     renderRows : function(startRow, endRow){
54167         // pull in all the crap needed to render rows
54168         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54169         var colCount = cm.getColumnCount();
54170
54171         if(ds.getCount() < 1){
54172             return ["", ""];
54173         }
54174
54175         // build a map for all the columns
54176         var cs = [];
54177         for(var i = 0; i < colCount; i++){
54178             var name = cm.getDataIndex(i);
54179             cs[i] = {
54180                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54181                 renderer : cm.getRenderer(i),
54182                 id : cm.getColumnId(i),
54183                 locked : cm.isLocked(i)
54184             };
54185         }
54186
54187         startRow = startRow || 0;
54188         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54189
54190         // records to render
54191         var rs = ds.getRange(startRow, endRow);
54192
54193         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54194     },
54195
54196     // As much as I hate to duplicate code, this was branched because FireFox really hates
54197     // [].join("") on strings. The performance difference was substantial enough to
54198     // branch this function
54199     doRender : Roo.isGecko ?
54200             function(cs, rs, ds, startRow, colCount, stripe){
54201                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54202                 // buffers
54203                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54204                 
54205                 var hasListener = this.grid.hasListener('rowclass');
54206                 var rowcfg = {};
54207                 for(var j = 0, len = rs.length; j < len; j++){
54208                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54209                     for(var i = 0; i < colCount; i++){
54210                         c = cs[i];
54211                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54212                         p.id = c.id;
54213                         p.css = p.attr = "";
54214                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54215                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54216                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54217                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54218                         }
54219                         var markup = ct.apply(p);
54220                         if(!c.locked){
54221                             cb+= markup;
54222                         }else{
54223                             lcb+= markup;
54224                         }
54225                     }
54226                     var alt = [];
54227                     if(stripe && ((rowIndex+1) % 2 == 0)){
54228                         alt.push("x-grid-row-alt")
54229                     }
54230                     if(r.dirty){
54231                         alt.push(  " x-grid-dirty-row");
54232                     }
54233                     rp.cells = lcb;
54234                     if(this.getRowClass){
54235                         alt.push(this.getRowClass(r, rowIndex));
54236                     }
54237                     if (hasListener) {
54238                         rowcfg = {
54239                              
54240                             record: r,
54241                             rowIndex : rowIndex,
54242                             rowClass : ''
54243                         }
54244                         this.grid.fireEvent('rowclass', this, rowcfg);
54245                         alt.push(rowcfg.rowClass);
54246                     }
54247                     rp.alt = alt.join(" ");
54248                     lbuf+= rt.apply(rp);
54249                     rp.cells = cb;
54250                     buf+=  rt.apply(rp);
54251                 }
54252                 return [lbuf, buf];
54253             } :
54254             function(cs, rs, ds, startRow, colCount, stripe){
54255                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54256                 // buffers
54257                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54258                 var hasListener = this.grid.hasListener('rowclass');
54259  
54260                 var rowcfg = {};
54261                 for(var j = 0, len = rs.length; j < len; j++){
54262                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54263                     for(var i = 0; i < colCount; i++){
54264                         c = cs[i];
54265                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54266                         p.id = c.id;
54267                         p.css = p.attr = "";
54268                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54269                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54270                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54271                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54272                         }
54273                         
54274                         var markup = ct.apply(p);
54275                         if(!c.locked){
54276                             cb[cb.length] = markup;
54277                         }else{
54278                             lcb[lcb.length] = markup;
54279                         }
54280                     }
54281                     var alt = [];
54282                     if(stripe && ((rowIndex+1) % 2 == 0)){
54283                         alt.push( "x-grid-row-alt");
54284                     }
54285                     if(r.dirty){
54286                         alt.push(" x-grid-dirty-row");
54287                     }
54288                     rp.cells = lcb;
54289                     if(this.getRowClass){
54290                         alt.push( this.getRowClass(r, rowIndex));
54291                     }
54292                     if (hasListener) {
54293                         rowcfg = {
54294                              
54295                             record: r,
54296                             rowIndex : rowIndex,
54297                             rowClass : ''
54298                         }
54299                         this.grid.fireEvent('rowclass', this, rowcfg);
54300                         alt.push(rowcfg.rowClass);
54301                     }
54302                     rp.alt = alt.join(" ");
54303                     rp.cells = lcb.join("");
54304                     lbuf[lbuf.length] = rt.apply(rp);
54305                     rp.cells = cb.join("");
54306                     buf[buf.length] =  rt.apply(rp);
54307                 }
54308                 return [lbuf.join(""), buf.join("")];
54309             },
54310
54311     renderBody : function(){
54312         var markup = this.renderRows();
54313         var bt = this.templates.body;
54314         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54315     },
54316
54317     /**
54318      * Refreshes the grid
54319      * @param {Boolean} headersToo
54320      */
54321     refresh : function(headersToo){
54322         this.fireEvent("beforerefresh", this);
54323         this.grid.stopEditing();
54324         var result = this.renderBody();
54325         this.lockedBody.update(result[0]);
54326         this.mainBody.update(result[1]);
54327         if(headersToo === true){
54328             this.updateHeaders();
54329             this.updateColumns();
54330             this.updateSplitters();
54331             this.updateHeaderSortState();
54332         }
54333         this.syncRowHeights();
54334         this.layout();
54335         this.fireEvent("refresh", this);
54336     },
54337
54338     handleColumnMove : function(cm, oldIndex, newIndex){
54339         this.indexMap = null;
54340         var s = this.getScrollState();
54341         this.refresh(true);
54342         this.restoreScroll(s);
54343         this.afterMove(newIndex);
54344     },
54345
54346     afterMove : function(colIndex){
54347         if(this.enableMoveAnim && Roo.enableFx){
54348             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54349         }
54350         // if multisort - fix sortOrder, and reload..
54351         if (this.grid.dataSource.multiSort) {
54352             // the we can call sort again..
54353             var dm = this.grid.dataSource;
54354             var cm = this.grid.colModel;
54355             var so = [];
54356             for(var i = 0; i < cm.config.length; i++ ) {
54357                 
54358                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54359                     continue; // dont' bother, it's not in sort list or being set.
54360                 }
54361                 
54362                 so.push(cm.config[i].dataIndex);
54363             };
54364             dm.sortOrder = so;
54365             dm.load(dm.lastOptions);
54366             
54367             
54368         }
54369         
54370     },
54371
54372     updateCell : function(dm, rowIndex, dataIndex){
54373         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54374         if(typeof colIndex == "undefined"){ // not present in grid
54375             return;
54376         }
54377         var cm = this.grid.colModel;
54378         var cell = this.getCell(rowIndex, colIndex);
54379         var cellText = this.getCellText(rowIndex, colIndex);
54380
54381         var p = {
54382             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54383             id : cm.getColumnId(colIndex),
54384             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54385         };
54386         var renderer = cm.getRenderer(colIndex);
54387         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54388         if(typeof val == "undefined" || val === "") val = "&#160;";
54389         cellText.innerHTML = val;
54390         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54391         this.syncRowHeights(rowIndex, rowIndex);
54392     },
54393
54394     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54395         var maxWidth = 0;
54396         if(this.grid.autoSizeHeaders){
54397             var h = this.getHeaderCellMeasure(colIndex);
54398             maxWidth = Math.max(maxWidth, h.scrollWidth);
54399         }
54400         var tb, index;
54401         if(this.cm.isLocked(colIndex)){
54402             tb = this.getLockedTable();
54403             index = colIndex;
54404         }else{
54405             tb = this.getBodyTable();
54406             index = colIndex - this.cm.getLockedCount();
54407         }
54408         if(tb && tb.rows){
54409             var rows = tb.rows;
54410             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54411             for(var i = 0; i < stopIndex; i++){
54412                 var cell = rows[i].childNodes[index].firstChild;
54413                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54414             }
54415         }
54416         return maxWidth + /*margin for error in IE*/ 5;
54417     },
54418     /**
54419      * Autofit a column to its content.
54420      * @param {Number} colIndex
54421      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54422      */
54423      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54424          if(this.cm.isHidden(colIndex)){
54425              return; // can't calc a hidden column
54426          }
54427         if(forceMinSize){
54428             var cid = this.cm.getColumnId(colIndex);
54429             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54430            if(this.grid.autoSizeHeaders){
54431                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54432            }
54433         }
54434         var newWidth = this.calcColumnWidth(colIndex);
54435         this.cm.setColumnWidth(colIndex,
54436             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54437         if(!suppressEvent){
54438             this.grid.fireEvent("columnresize", colIndex, newWidth);
54439         }
54440     },
54441
54442     /**
54443      * Autofits all columns to their content and then expands to fit any extra space in the grid
54444      */
54445      autoSizeColumns : function(){
54446         var cm = this.grid.colModel;
54447         var colCount = cm.getColumnCount();
54448         for(var i = 0; i < colCount; i++){
54449             this.autoSizeColumn(i, true, true);
54450         }
54451         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54452             this.fitColumns();
54453         }else{
54454             this.updateColumns();
54455             this.layout();
54456         }
54457     },
54458
54459     /**
54460      * Autofits all columns to the grid's width proportionate with their current size
54461      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54462      */
54463     fitColumns : function(reserveScrollSpace){
54464         var cm = this.grid.colModel;
54465         var colCount = cm.getColumnCount();
54466         var cols = [];
54467         var width = 0;
54468         var i, w;
54469         for (i = 0; i < colCount; i++){
54470             if(!cm.isHidden(i) && !cm.isFixed(i)){
54471                 w = cm.getColumnWidth(i);
54472                 cols.push(i);
54473                 cols.push(w);
54474                 width += w;
54475             }
54476         }
54477         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54478         if(reserveScrollSpace){
54479             avail -= 17;
54480         }
54481         var frac = (avail - cm.getTotalWidth())/width;
54482         while (cols.length){
54483             w = cols.pop();
54484             i = cols.pop();
54485             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54486         }
54487         this.updateColumns();
54488         this.layout();
54489     },
54490
54491     onRowSelect : function(rowIndex){
54492         var row = this.getRowComposite(rowIndex);
54493         row.addClass("x-grid-row-selected");
54494     },
54495
54496     onRowDeselect : function(rowIndex){
54497         var row = this.getRowComposite(rowIndex);
54498         row.removeClass("x-grid-row-selected");
54499     },
54500
54501     onCellSelect : function(row, col){
54502         var cell = this.getCell(row, col);
54503         if(cell){
54504             Roo.fly(cell).addClass("x-grid-cell-selected");
54505         }
54506     },
54507
54508     onCellDeselect : function(row, col){
54509         var cell = this.getCell(row, col);
54510         if(cell){
54511             Roo.fly(cell).removeClass("x-grid-cell-selected");
54512         }
54513     },
54514
54515     updateHeaderSortState : function(){
54516         
54517         // sort state can be single { field: xxx, direction : yyy}
54518         // or   { xxx=>ASC , yyy : DESC ..... }
54519         
54520         var mstate = {};
54521         if (!this.ds.multiSort) { 
54522             var state = this.ds.getSortState();
54523             if(!state){
54524                 return;
54525             }
54526             mstate[state.field] = state.direction;
54527             // FIXME... - this is not used here.. but might be elsewhere..
54528             this.sortState = state;
54529             
54530         } else {
54531             mstate = this.ds.sortToggle;
54532         }
54533         //remove existing sort classes..
54534         
54535         var sc = this.sortClasses;
54536         var hds = this.el.select(this.headerSelector).removeClass(sc);
54537         
54538         for(var f in mstate) {
54539         
54540             var sortColumn = this.cm.findColumnIndex(f);
54541             
54542             if(sortColumn != -1){
54543                 var sortDir = mstate[f];        
54544                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54545             }
54546         }
54547         
54548          
54549         
54550     },
54551
54552
54553     handleHeaderClick : function(g, index,e){
54554         
54555         Roo.log("header click");
54556         
54557         if (Roo.isTouch) {
54558             // touch events on header are handled by context
54559             this.handleHdCtx(g,index,e);
54560             return;
54561         }
54562         
54563         
54564         if(this.headersDisabled){
54565             return;
54566         }
54567         var dm = g.dataSource, cm = g.colModel;
54568         if(!cm.isSortable(index)){
54569             return;
54570         }
54571         g.stopEditing();
54572         
54573         if (dm.multiSort) {
54574             // update the sortOrder
54575             var so = [];
54576             for(var i = 0; i < cm.config.length; i++ ) {
54577                 
54578                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54579                     continue; // dont' bother, it's not in sort list or being set.
54580                 }
54581                 
54582                 so.push(cm.config[i].dataIndex);
54583             };
54584             dm.sortOrder = so;
54585         }
54586         
54587         
54588         dm.sort(cm.getDataIndex(index));
54589     },
54590
54591
54592     destroy : function(){
54593         if(this.colMenu){
54594             this.colMenu.removeAll();
54595             Roo.menu.MenuMgr.unregister(this.colMenu);
54596             this.colMenu.getEl().remove();
54597             delete this.colMenu;
54598         }
54599         if(this.hmenu){
54600             this.hmenu.removeAll();
54601             Roo.menu.MenuMgr.unregister(this.hmenu);
54602             this.hmenu.getEl().remove();
54603             delete this.hmenu;
54604         }
54605         if(this.grid.enableColumnMove){
54606             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54607             if(dds){
54608                 for(var dd in dds){
54609                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54610                         var elid = dds[dd].dragElId;
54611                         dds[dd].unreg();
54612                         Roo.get(elid).remove();
54613                     } else if(dds[dd].config.isTarget){
54614                         dds[dd].proxyTop.remove();
54615                         dds[dd].proxyBottom.remove();
54616                         dds[dd].unreg();
54617                     }
54618                     if(Roo.dd.DDM.locationCache[dd]){
54619                         delete Roo.dd.DDM.locationCache[dd];
54620                     }
54621                 }
54622                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54623             }
54624         }
54625         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54626         this.bind(null, null);
54627         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54628     },
54629
54630     handleLockChange : function(){
54631         this.refresh(true);
54632     },
54633
54634     onDenyColumnLock : function(){
54635
54636     },
54637
54638     onDenyColumnHide : function(){
54639
54640     },
54641
54642     handleHdMenuClick : function(item){
54643         var index = this.hdCtxIndex;
54644         var cm = this.cm, ds = this.ds;
54645         switch(item.id){
54646             case "asc":
54647                 ds.sort(cm.getDataIndex(index), "ASC");
54648                 break;
54649             case "desc":
54650                 ds.sort(cm.getDataIndex(index), "DESC");
54651                 break;
54652             case "lock":
54653                 var lc = cm.getLockedCount();
54654                 if(cm.getColumnCount(true) <= lc+1){
54655                     this.onDenyColumnLock();
54656                     return;
54657                 }
54658                 if(lc != index){
54659                     cm.setLocked(index, true, true);
54660                     cm.moveColumn(index, lc);
54661                     this.grid.fireEvent("columnmove", index, lc);
54662                 }else{
54663                     cm.setLocked(index, true);
54664                 }
54665             break;
54666             case "unlock":
54667                 var lc = cm.getLockedCount();
54668                 if((lc-1) != index){
54669                     cm.setLocked(index, false, true);
54670                     cm.moveColumn(index, lc-1);
54671                     this.grid.fireEvent("columnmove", index, lc-1);
54672                 }else{
54673                     cm.setLocked(index, false);
54674                 }
54675             break;
54676             case 'wider': // used to expand cols on touch..
54677             case 'narrow':
54678                 var cw = cm.getColumnWidth(index);
54679                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54680                 cw = Math.max(0, cw);
54681                 cw = Math.min(cw,4000);
54682                 cm.setColumnWidth(index, cw);
54683                 break;
54684                 
54685             default:
54686                 index = cm.getIndexById(item.id.substr(4));
54687                 if(index != -1){
54688                     if(item.checked && cm.getColumnCount(true) <= 1){
54689                         this.onDenyColumnHide();
54690                         return false;
54691                     }
54692                     cm.setHidden(index, item.checked);
54693                 }
54694         }
54695         return true;
54696     },
54697
54698     beforeColMenuShow : function(){
54699         var cm = this.cm,  colCount = cm.getColumnCount();
54700         this.colMenu.removeAll();
54701         for(var i = 0; i < colCount; i++){
54702             this.colMenu.add(new Roo.menu.CheckItem({
54703                 id: "col-"+cm.getColumnId(i),
54704                 text: cm.getColumnHeader(i),
54705                 checked: !cm.isHidden(i),
54706                 hideOnClick:false
54707             }));
54708         }
54709     },
54710
54711     handleHdCtx : function(g, index, e){
54712         e.stopEvent();
54713         var hd = this.getHeaderCell(index);
54714         this.hdCtxIndex = index;
54715         var ms = this.hmenu.items, cm = this.cm;
54716         ms.get("asc").setDisabled(!cm.isSortable(index));
54717         ms.get("desc").setDisabled(!cm.isSortable(index));
54718         if(this.grid.enableColLock !== false){
54719             ms.get("lock").setDisabled(cm.isLocked(index));
54720             ms.get("unlock").setDisabled(!cm.isLocked(index));
54721         }
54722         this.hmenu.show(hd, "tl-bl");
54723     },
54724
54725     handleHdOver : function(e){
54726         var hd = this.findHeaderCell(e.getTarget());
54727         if(hd && !this.headersDisabled){
54728             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54729                this.fly(hd).addClass("x-grid-hd-over");
54730             }
54731         }
54732     },
54733
54734     handleHdOut : function(e){
54735         var hd = this.findHeaderCell(e.getTarget());
54736         if(hd){
54737             this.fly(hd).removeClass("x-grid-hd-over");
54738         }
54739     },
54740
54741     handleSplitDblClick : function(e, t){
54742         var i = this.getCellIndex(t);
54743         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54744             this.autoSizeColumn(i, true);
54745             this.layout();
54746         }
54747     },
54748
54749     render : function(){
54750
54751         var cm = this.cm;
54752         var colCount = cm.getColumnCount();
54753
54754         if(this.grid.monitorWindowResize === true){
54755             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54756         }
54757         var header = this.renderHeaders();
54758         var body = this.templates.body.apply({rows:""});
54759         var html = this.templates.master.apply({
54760             lockedBody: body,
54761             body: body,
54762             lockedHeader: header[0],
54763             header: header[1]
54764         });
54765
54766         //this.updateColumns();
54767
54768         this.grid.getGridEl().dom.innerHTML = html;
54769
54770         this.initElements();
54771         
54772         // a kludge to fix the random scolling effect in webkit
54773         this.el.on("scroll", function() {
54774             this.el.dom.scrollTop=0; // hopefully not recursive..
54775         },this);
54776
54777         this.scroller.on("scroll", this.handleScroll, this);
54778         this.lockedBody.on("mousewheel", this.handleWheel, this);
54779         this.mainBody.on("mousewheel", this.handleWheel, this);
54780
54781         this.mainHd.on("mouseover", this.handleHdOver, this);
54782         this.mainHd.on("mouseout", this.handleHdOut, this);
54783         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54784                 {delegate: "."+this.splitClass});
54785
54786         this.lockedHd.on("mouseover", this.handleHdOver, this);
54787         this.lockedHd.on("mouseout", this.handleHdOut, this);
54788         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54789                 {delegate: "."+this.splitClass});
54790
54791         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54792             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54793         }
54794
54795         this.updateSplitters();
54796
54797         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54798             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54799             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54800         }
54801
54802         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54803             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54804             this.hmenu.add(
54805                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54806                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54807             );
54808             if(this.grid.enableColLock !== false){
54809                 this.hmenu.add('-',
54810                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54811                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54812                 );
54813             }
54814             if (Roo.isTouch) {
54815                  this.hmenu.add('-',
54816                     {id:"wider", text: this.columnsWiderText},
54817                     {id:"narrow", text: this.columnsNarrowText }
54818                 );
54819                 
54820                  
54821             }
54822             
54823             if(this.grid.enableColumnHide !== false){
54824
54825                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54826                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54827                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54828
54829                 this.hmenu.add('-',
54830                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54831                 );
54832             }
54833             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54834
54835             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54836         }
54837
54838         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54839             this.dd = new Roo.grid.GridDragZone(this.grid, {
54840                 ddGroup : this.grid.ddGroup || 'GridDD'
54841             });
54842             
54843         }
54844
54845         /*
54846         for(var i = 0; i < colCount; i++){
54847             if(cm.isHidden(i)){
54848                 this.hideColumn(i);
54849             }
54850             if(cm.config[i].align){
54851                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54852                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54853             }
54854         }*/
54855         
54856         this.updateHeaderSortState();
54857
54858         this.beforeInitialResize();
54859         this.layout(true);
54860
54861         // two part rendering gives faster view to the user
54862         this.renderPhase2.defer(1, this);
54863     },
54864
54865     renderPhase2 : function(){
54866         // render the rows now
54867         this.refresh();
54868         if(this.grid.autoSizeColumns){
54869             this.autoSizeColumns();
54870         }
54871     },
54872
54873     beforeInitialResize : function(){
54874
54875     },
54876
54877     onColumnSplitterMoved : function(i, w){
54878         this.userResized = true;
54879         var cm = this.grid.colModel;
54880         cm.setColumnWidth(i, w, true);
54881         var cid = cm.getColumnId(i);
54882         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54883         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54884         this.updateSplitters();
54885         this.layout();
54886         this.grid.fireEvent("columnresize", i, w);
54887     },
54888
54889     syncRowHeights : function(startIndex, endIndex){
54890         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54891             startIndex = startIndex || 0;
54892             var mrows = this.getBodyTable().rows;
54893             var lrows = this.getLockedTable().rows;
54894             var len = mrows.length-1;
54895             endIndex = Math.min(endIndex || len, len);
54896             for(var i = startIndex; i <= endIndex; i++){
54897                 var m = mrows[i], l = lrows[i];
54898                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54899                 m.style.height = l.style.height = h + "px";
54900             }
54901         }
54902     },
54903
54904     layout : function(initialRender, is2ndPass){
54905         var g = this.grid;
54906         var auto = g.autoHeight;
54907         var scrollOffset = 16;
54908         var c = g.getGridEl(), cm = this.cm,
54909                 expandCol = g.autoExpandColumn,
54910                 gv = this;
54911         //c.beginMeasure();
54912
54913         if(!c.dom.offsetWidth){ // display:none?
54914             if(initialRender){
54915                 this.lockedWrap.show();
54916                 this.mainWrap.show();
54917             }
54918             return;
54919         }
54920
54921         var hasLock = this.cm.isLocked(0);
54922
54923         var tbh = this.headerPanel.getHeight();
54924         var bbh = this.footerPanel.getHeight();
54925
54926         if(auto){
54927             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54928             var newHeight = ch + c.getBorderWidth("tb");
54929             if(g.maxHeight){
54930                 newHeight = Math.min(g.maxHeight, newHeight);
54931             }
54932             c.setHeight(newHeight);
54933         }
54934
54935         if(g.autoWidth){
54936             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54937         }
54938
54939         var s = this.scroller;
54940
54941         var csize = c.getSize(true);
54942
54943         this.el.setSize(csize.width, csize.height);
54944
54945         this.headerPanel.setWidth(csize.width);
54946         this.footerPanel.setWidth(csize.width);
54947
54948         var hdHeight = this.mainHd.getHeight();
54949         var vw = csize.width;
54950         var vh = csize.height - (tbh + bbh);
54951
54952         s.setSize(vw, vh);
54953
54954         var bt = this.getBodyTable();
54955         var ltWidth = hasLock ?
54956                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54957
54958         var scrollHeight = bt.offsetHeight;
54959         var scrollWidth = ltWidth + bt.offsetWidth;
54960         var vscroll = false, hscroll = false;
54961
54962         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54963
54964         var lw = this.lockedWrap, mw = this.mainWrap;
54965         var lb = this.lockedBody, mb = this.mainBody;
54966
54967         setTimeout(function(){
54968             var t = s.dom.offsetTop;
54969             var w = s.dom.clientWidth,
54970                 h = s.dom.clientHeight;
54971
54972             lw.setTop(t);
54973             lw.setSize(ltWidth, h);
54974
54975             mw.setLeftTop(ltWidth, t);
54976             mw.setSize(w-ltWidth, h);
54977
54978             lb.setHeight(h-hdHeight);
54979             mb.setHeight(h-hdHeight);
54980
54981             if(is2ndPass !== true && !gv.userResized && expandCol){
54982                 // high speed resize without full column calculation
54983                 
54984                 var ci = cm.getIndexById(expandCol);
54985                 if (ci < 0) {
54986                     ci = cm.findColumnIndex(expandCol);
54987                 }
54988                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54989                 var expandId = cm.getColumnId(ci);
54990                 var  tw = cm.getTotalWidth(false);
54991                 var currentWidth = cm.getColumnWidth(ci);
54992                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54993                 if(currentWidth != cw){
54994                     cm.setColumnWidth(ci, cw, true);
54995                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54996                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54997                     gv.updateSplitters();
54998                     gv.layout(false, true);
54999                 }
55000             }
55001
55002             if(initialRender){
55003                 lw.show();
55004                 mw.show();
55005             }
55006             //c.endMeasure();
55007         }, 10);
55008     },
55009
55010     onWindowResize : function(){
55011         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55012             return;
55013         }
55014         this.layout();
55015     },
55016
55017     appendFooter : function(parentEl){
55018         return null;
55019     },
55020
55021     sortAscText : "Sort Ascending",
55022     sortDescText : "Sort Descending",
55023     lockText : "Lock Column",
55024     unlockText : "Unlock Column",
55025     columnsText : "Columns",
55026  
55027     columnsWiderText : "Wider",
55028     columnsNarrowText : "Thinner"
55029 });
55030
55031
55032 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55033     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55034     this.proxy.el.addClass('x-grid3-col-dd');
55035 };
55036
55037 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55038     handleMouseDown : function(e){
55039
55040     },
55041
55042     callHandleMouseDown : function(e){
55043         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55044     }
55045 });
55046 /*
55047  * Based on:
55048  * Ext JS Library 1.1.1
55049  * Copyright(c) 2006-2007, Ext JS, LLC.
55050  *
55051  * Originally Released Under LGPL - original licence link has changed is not relivant.
55052  *
55053  * Fork - LGPL
55054  * <script type="text/javascript">
55055  */
55056  
55057 // private
55058 // This is a support class used internally by the Grid components
55059 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55060     this.grid = grid;
55061     this.view = grid.getView();
55062     this.proxy = this.view.resizeProxy;
55063     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55064         "gridSplitters" + this.grid.getGridEl().id, {
55065         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55066     });
55067     this.setHandleElId(Roo.id(hd));
55068     this.setOuterHandleElId(Roo.id(hd2));
55069     this.scroll = false;
55070 };
55071 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55072     fly: Roo.Element.fly,
55073
55074     b4StartDrag : function(x, y){
55075         this.view.headersDisabled = true;
55076         this.proxy.setHeight(this.view.mainWrap.getHeight());
55077         var w = this.cm.getColumnWidth(this.cellIndex);
55078         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55079         this.resetConstraints();
55080         this.setXConstraint(minw, 1000);
55081         this.setYConstraint(0, 0);
55082         this.minX = x - minw;
55083         this.maxX = x + 1000;
55084         this.startPos = x;
55085         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55086     },
55087
55088
55089     handleMouseDown : function(e){
55090         ev = Roo.EventObject.setEvent(e);
55091         var t = this.fly(ev.getTarget());
55092         if(t.hasClass("x-grid-split")){
55093             this.cellIndex = this.view.getCellIndex(t.dom);
55094             this.split = t.dom;
55095             this.cm = this.grid.colModel;
55096             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55097                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55098             }
55099         }
55100     },
55101
55102     endDrag : function(e){
55103         this.view.headersDisabled = false;
55104         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55105         var diff = endX - this.startPos;
55106         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55107     },
55108
55109     autoOffset : function(){
55110         this.setDelta(0,0);
55111     }
55112 });/*
55113  * Based on:
55114  * Ext JS Library 1.1.1
55115  * Copyright(c) 2006-2007, Ext JS, LLC.
55116  *
55117  * Originally Released Under LGPL - original licence link has changed is not relivant.
55118  *
55119  * Fork - LGPL
55120  * <script type="text/javascript">
55121  */
55122  
55123 // private
55124 // This is a support class used internally by the Grid components
55125 Roo.grid.GridDragZone = function(grid, config){
55126     this.view = grid.getView();
55127     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55128     if(this.view.lockedBody){
55129         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55130         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55131     }
55132     this.scroll = false;
55133     this.grid = grid;
55134     this.ddel = document.createElement('div');
55135     this.ddel.className = 'x-grid-dd-wrap';
55136 };
55137
55138 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55139     ddGroup : "GridDD",
55140
55141     getDragData : function(e){
55142         var t = Roo.lib.Event.getTarget(e);
55143         var rowIndex = this.view.findRowIndex(t);
55144         var sm = this.grid.selModel;
55145             
55146         //Roo.log(rowIndex);
55147         
55148         if (sm.getSelectedCell) {
55149             // cell selection..
55150             if (!sm.getSelectedCell()) {
55151                 return false;
55152             }
55153             if (rowIndex != sm.getSelectedCell()[0]) {
55154                 return false;
55155             }
55156         
55157         }
55158         
55159         if(rowIndex !== false){
55160             
55161             // if editorgrid.. 
55162             
55163             
55164             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55165                
55166             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55167               //  
55168             //}
55169             if (e.hasModifier()){
55170                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55171             }
55172             
55173             Roo.log("getDragData");
55174             
55175             return {
55176                 grid: this.grid,
55177                 ddel: this.ddel,
55178                 rowIndex: rowIndex,
55179                 selections:sm.getSelections ? sm.getSelections() : (
55180                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55181                 )
55182             };
55183         }
55184         return false;
55185     },
55186
55187     onInitDrag : function(e){
55188         var data = this.dragData;
55189         this.ddel.innerHTML = this.grid.getDragDropText();
55190         this.proxy.update(this.ddel);
55191         // fire start drag?
55192     },
55193
55194     afterRepair : function(){
55195         this.dragging = false;
55196     },
55197
55198     getRepairXY : function(e, data){
55199         return false;
55200     },
55201
55202     onEndDrag : function(data, e){
55203         // fire end drag?
55204     },
55205
55206     onValidDrop : function(dd, e, id){
55207         // fire drag drop?
55208         this.hideProxy();
55209     },
55210
55211     beforeInvalidDrop : function(e, id){
55212
55213     }
55214 });/*
55215  * Based on:
55216  * Ext JS Library 1.1.1
55217  * Copyright(c) 2006-2007, Ext JS, LLC.
55218  *
55219  * Originally Released Under LGPL - original licence link has changed is not relivant.
55220  *
55221  * Fork - LGPL
55222  * <script type="text/javascript">
55223  */
55224  
55225
55226 /**
55227  * @class Roo.grid.ColumnModel
55228  * @extends Roo.util.Observable
55229  * This is the default implementation of a ColumnModel used by the Grid. It defines
55230  * the columns in the grid.
55231  * <br>Usage:<br>
55232  <pre><code>
55233  var colModel = new Roo.grid.ColumnModel([
55234         {header: "Ticker", width: 60, sortable: true, locked: true},
55235         {header: "Company Name", width: 150, sortable: true},
55236         {header: "Market Cap.", width: 100, sortable: true},
55237         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55238         {header: "Employees", width: 100, sortable: true, resizable: false}
55239  ]);
55240  </code></pre>
55241  * <p>
55242  
55243  * The config options listed for this class are options which may appear in each
55244  * individual column definition.
55245  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55246  * @constructor
55247  * @param {Object} config An Array of column config objects. See this class's
55248  * config objects for details.
55249 */
55250 Roo.grid.ColumnModel = function(config){
55251         /**
55252      * The config passed into the constructor
55253      */
55254     this.config = config;
55255     this.lookup = {};
55256
55257     // if no id, create one
55258     // if the column does not have a dataIndex mapping,
55259     // map it to the order it is in the config
55260     for(var i = 0, len = config.length; i < len; i++){
55261         var c = config[i];
55262         if(typeof c.dataIndex == "undefined"){
55263             c.dataIndex = i;
55264         }
55265         if(typeof c.renderer == "string"){
55266             c.renderer = Roo.util.Format[c.renderer];
55267         }
55268         if(typeof c.id == "undefined"){
55269             c.id = Roo.id();
55270         }
55271         if(c.editor && c.editor.xtype){
55272             c.editor  = Roo.factory(c.editor, Roo.grid);
55273         }
55274         if(c.editor && c.editor.isFormField){
55275             c.editor = new Roo.grid.GridEditor(c.editor);
55276         }
55277         this.lookup[c.id] = c;
55278     }
55279
55280     /**
55281      * The width of columns which have no width specified (defaults to 100)
55282      * @type Number
55283      */
55284     this.defaultWidth = 100;
55285
55286     /**
55287      * Default sortable of columns which have no sortable specified (defaults to false)
55288      * @type Boolean
55289      */
55290     this.defaultSortable = false;
55291
55292     this.addEvents({
55293         /**
55294              * @event widthchange
55295              * Fires when the width of a column changes.
55296              * @param {ColumnModel} this
55297              * @param {Number} columnIndex The column index
55298              * @param {Number} newWidth The new width
55299              */
55300             "widthchange": true,
55301         /**
55302              * @event headerchange
55303              * Fires when the text of a header changes.
55304              * @param {ColumnModel} this
55305              * @param {Number} columnIndex The column index
55306              * @param {Number} newText The new header text
55307              */
55308             "headerchange": true,
55309         /**
55310              * @event hiddenchange
55311              * Fires when a column is hidden or "unhidden".
55312              * @param {ColumnModel} this
55313              * @param {Number} columnIndex The column index
55314              * @param {Boolean} hidden true if hidden, false otherwise
55315              */
55316             "hiddenchange": true,
55317             /**
55318          * @event columnmoved
55319          * Fires when a column is moved.
55320          * @param {ColumnModel} this
55321          * @param {Number} oldIndex
55322          * @param {Number} newIndex
55323          */
55324         "columnmoved" : true,
55325         /**
55326          * @event columlockchange
55327          * Fires when a column's locked state is changed
55328          * @param {ColumnModel} this
55329          * @param {Number} colIndex
55330          * @param {Boolean} locked true if locked
55331          */
55332         "columnlockchange" : true
55333     });
55334     Roo.grid.ColumnModel.superclass.constructor.call(this);
55335 };
55336 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55337     /**
55338      * @cfg {String} header The header text to display in the Grid view.
55339      */
55340     /**
55341      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55342      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55343      * specified, the column's index is used as an index into the Record's data Array.
55344      */
55345     /**
55346      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55347      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55348      */
55349     /**
55350      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55351      * Defaults to the value of the {@link #defaultSortable} property.
55352      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55353      */
55354     /**
55355      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55356      */
55357     /**
55358      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55359      */
55360     /**
55361      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55362      */
55363     /**
55364      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55365      */
55366     /**
55367      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55368      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55369      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55370      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55371      */
55372        /**
55373      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55374      */
55375     /**
55376      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55377      */
55378     /**
55379      * @cfg {String} cursor (Optional)
55380      */
55381     /**
55382      * @cfg {String} tooltip (Optional)
55383      */
55384     /**
55385      * Returns the id of the column at the specified index.
55386      * @param {Number} index The column index
55387      * @return {String} the id
55388      */
55389     getColumnId : function(index){
55390         return this.config[index].id;
55391     },
55392
55393     /**
55394      * Returns the column for a specified id.
55395      * @param {String} id The column id
55396      * @return {Object} the column
55397      */
55398     getColumnById : function(id){
55399         return this.lookup[id];
55400     },
55401
55402     
55403     /**
55404      * Returns the column for a specified dataIndex.
55405      * @param {String} dataIndex The column dataIndex
55406      * @return {Object|Boolean} the column or false if not found
55407      */
55408     getColumnByDataIndex: function(dataIndex){
55409         var index = this.findColumnIndex(dataIndex);
55410         return index > -1 ? this.config[index] : false;
55411     },
55412     
55413     /**
55414      * Returns the index for a specified column id.
55415      * @param {String} id The column id
55416      * @return {Number} the index, or -1 if not found
55417      */
55418     getIndexById : function(id){
55419         for(var i = 0, len = this.config.length; i < len; i++){
55420             if(this.config[i].id == id){
55421                 return i;
55422             }
55423         }
55424         return -1;
55425     },
55426     
55427     /**
55428      * Returns the index for a specified column dataIndex.
55429      * @param {String} dataIndex The column dataIndex
55430      * @return {Number} the index, or -1 if not found
55431      */
55432     
55433     findColumnIndex : function(dataIndex){
55434         for(var i = 0, len = this.config.length; i < len; i++){
55435             if(this.config[i].dataIndex == dataIndex){
55436                 return i;
55437             }
55438         }
55439         return -1;
55440     },
55441     
55442     
55443     moveColumn : function(oldIndex, newIndex){
55444         var c = this.config[oldIndex];
55445         this.config.splice(oldIndex, 1);
55446         this.config.splice(newIndex, 0, c);
55447         this.dataMap = null;
55448         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55449     },
55450
55451     isLocked : function(colIndex){
55452         return this.config[colIndex].locked === true;
55453     },
55454
55455     setLocked : function(colIndex, value, suppressEvent){
55456         if(this.isLocked(colIndex) == value){
55457             return;
55458         }
55459         this.config[colIndex].locked = value;
55460         if(!suppressEvent){
55461             this.fireEvent("columnlockchange", this, colIndex, value);
55462         }
55463     },
55464
55465     getTotalLockedWidth : function(){
55466         var totalWidth = 0;
55467         for(var i = 0; i < this.config.length; i++){
55468             if(this.isLocked(i) && !this.isHidden(i)){
55469                 this.totalWidth += this.getColumnWidth(i);
55470             }
55471         }
55472         return totalWidth;
55473     },
55474
55475     getLockedCount : function(){
55476         for(var i = 0, len = this.config.length; i < len; i++){
55477             if(!this.isLocked(i)){
55478                 return i;
55479             }
55480         }
55481     },
55482
55483     /**
55484      * Returns the number of columns.
55485      * @return {Number}
55486      */
55487     getColumnCount : function(visibleOnly){
55488         if(visibleOnly === true){
55489             var c = 0;
55490             for(var i = 0, len = this.config.length; i < len; i++){
55491                 if(!this.isHidden(i)){
55492                     c++;
55493                 }
55494             }
55495             return c;
55496         }
55497         return this.config.length;
55498     },
55499
55500     /**
55501      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55502      * @param {Function} fn
55503      * @param {Object} scope (optional)
55504      * @return {Array} result
55505      */
55506     getColumnsBy : function(fn, scope){
55507         var r = [];
55508         for(var i = 0, len = this.config.length; i < len; i++){
55509             var c = this.config[i];
55510             if(fn.call(scope||this, c, i) === true){
55511                 r[r.length] = c;
55512             }
55513         }
55514         return r;
55515     },
55516
55517     /**
55518      * Returns true if the specified column is sortable.
55519      * @param {Number} col The column index
55520      * @return {Boolean}
55521      */
55522     isSortable : function(col){
55523         if(typeof this.config[col].sortable == "undefined"){
55524             return this.defaultSortable;
55525         }
55526         return this.config[col].sortable;
55527     },
55528
55529     /**
55530      * Returns the rendering (formatting) function defined for the column.
55531      * @param {Number} col The column index.
55532      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55533      */
55534     getRenderer : function(col){
55535         if(!this.config[col].renderer){
55536             return Roo.grid.ColumnModel.defaultRenderer;
55537         }
55538         return this.config[col].renderer;
55539     },
55540
55541     /**
55542      * Sets the rendering (formatting) function for a column.
55543      * @param {Number} col The column index
55544      * @param {Function} fn The function to use to process the cell's raw data
55545      * to return HTML markup for the grid view. The render function is called with
55546      * the following parameters:<ul>
55547      * <li>Data value.</li>
55548      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55549      * <li>css A CSS style string to apply to the table cell.</li>
55550      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55551      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55552      * <li>Row index</li>
55553      * <li>Column index</li>
55554      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55555      */
55556     setRenderer : function(col, fn){
55557         this.config[col].renderer = fn;
55558     },
55559
55560     /**
55561      * Returns the width for the specified column.
55562      * @param {Number} col The column index
55563      * @return {Number}
55564      */
55565     getColumnWidth : function(col){
55566         return this.config[col].width * 1 || this.defaultWidth;
55567     },
55568
55569     /**
55570      * Sets the width for a column.
55571      * @param {Number} col The column index
55572      * @param {Number} width The new width
55573      */
55574     setColumnWidth : function(col, width, suppressEvent){
55575         this.config[col].width = width;
55576         this.totalWidth = null;
55577         if(!suppressEvent){
55578              this.fireEvent("widthchange", this, col, width);
55579         }
55580     },
55581
55582     /**
55583      * Returns the total width of all columns.
55584      * @param {Boolean} includeHidden True to include hidden column widths
55585      * @return {Number}
55586      */
55587     getTotalWidth : function(includeHidden){
55588         if(!this.totalWidth){
55589             this.totalWidth = 0;
55590             for(var i = 0, len = this.config.length; i < len; i++){
55591                 if(includeHidden || !this.isHidden(i)){
55592                     this.totalWidth += this.getColumnWidth(i);
55593                 }
55594             }
55595         }
55596         return this.totalWidth;
55597     },
55598
55599     /**
55600      * Returns the header for the specified column.
55601      * @param {Number} col The column index
55602      * @return {String}
55603      */
55604     getColumnHeader : function(col){
55605         return this.config[col].header;
55606     },
55607
55608     /**
55609      * Sets the header for a column.
55610      * @param {Number} col The column index
55611      * @param {String} header The new header
55612      */
55613     setColumnHeader : function(col, header){
55614         this.config[col].header = header;
55615         this.fireEvent("headerchange", this, col, header);
55616     },
55617
55618     /**
55619      * Returns the tooltip for the specified column.
55620      * @param {Number} col The column index
55621      * @return {String}
55622      */
55623     getColumnTooltip : function(col){
55624             return this.config[col].tooltip;
55625     },
55626     /**
55627      * Sets the tooltip for a column.
55628      * @param {Number} col The column index
55629      * @param {String} tooltip The new tooltip
55630      */
55631     setColumnTooltip : function(col, tooltip){
55632             this.config[col].tooltip = tooltip;
55633     },
55634
55635     /**
55636      * Returns the dataIndex for the specified column.
55637      * @param {Number} col The column index
55638      * @return {Number}
55639      */
55640     getDataIndex : function(col){
55641         return this.config[col].dataIndex;
55642     },
55643
55644     /**
55645      * Sets the dataIndex for a column.
55646      * @param {Number} col The column index
55647      * @param {Number} dataIndex The new dataIndex
55648      */
55649     setDataIndex : function(col, dataIndex){
55650         this.config[col].dataIndex = dataIndex;
55651     },
55652
55653     
55654     
55655     /**
55656      * Returns true if the cell is editable.
55657      * @param {Number} colIndex The column index
55658      * @param {Number} rowIndex The row index
55659      * @return {Boolean}
55660      */
55661     isCellEditable : function(colIndex, rowIndex){
55662         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55663     },
55664
55665     /**
55666      * Returns the editor defined for the cell/column.
55667      * return false or null to disable editing.
55668      * @param {Number} colIndex The column index
55669      * @param {Number} rowIndex The row index
55670      * @return {Object}
55671      */
55672     getCellEditor : function(colIndex, rowIndex){
55673         return this.config[colIndex].editor;
55674     },
55675
55676     /**
55677      * Sets if a column is editable.
55678      * @param {Number} col The column index
55679      * @param {Boolean} editable True if the column is editable
55680      */
55681     setEditable : function(col, editable){
55682         this.config[col].editable = editable;
55683     },
55684
55685
55686     /**
55687      * Returns true if the column is hidden.
55688      * @param {Number} colIndex The column index
55689      * @return {Boolean}
55690      */
55691     isHidden : function(colIndex){
55692         return this.config[colIndex].hidden;
55693     },
55694
55695
55696     /**
55697      * Returns true if the column width cannot be changed
55698      */
55699     isFixed : function(colIndex){
55700         return this.config[colIndex].fixed;
55701     },
55702
55703     /**
55704      * Returns true if the column can be resized
55705      * @return {Boolean}
55706      */
55707     isResizable : function(colIndex){
55708         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55709     },
55710     /**
55711      * Sets if a column is hidden.
55712      * @param {Number} colIndex The column index
55713      * @param {Boolean} hidden True if the column is hidden
55714      */
55715     setHidden : function(colIndex, hidden){
55716         this.config[colIndex].hidden = hidden;
55717         this.totalWidth = null;
55718         this.fireEvent("hiddenchange", this, colIndex, hidden);
55719     },
55720
55721     /**
55722      * Sets the editor for a column.
55723      * @param {Number} col The column index
55724      * @param {Object} editor The editor object
55725      */
55726     setEditor : function(col, editor){
55727         this.config[col].editor = editor;
55728     }
55729 });
55730
55731 Roo.grid.ColumnModel.defaultRenderer = function(value){
55732         if(typeof value == "string" && value.length < 1){
55733             return "&#160;";
55734         }
55735         return value;
55736 };
55737
55738 // Alias for backwards compatibility
55739 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55740 /*
55741  * Based on:
55742  * Ext JS Library 1.1.1
55743  * Copyright(c) 2006-2007, Ext JS, LLC.
55744  *
55745  * Originally Released Under LGPL - original licence link has changed is not relivant.
55746  *
55747  * Fork - LGPL
55748  * <script type="text/javascript">
55749  */
55750
55751 /**
55752  * @class Roo.grid.AbstractSelectionModel
55753  * @extends Roo.util.Observable
55754  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55755  * implemented by descendant classes.  This class should not be directly instantiated.
55756  * @constructor
55757  */
55758 Roo.grid.AbstractSelectionModel = function(){
55759     this.locked = false;
55760     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55761 };
55762
55763 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55764     /** @ignore Called by the grid automatically. Do not call directly. */
55765     init : function(grid){
55766         this.grid = grid;
55767         this.initEvents();
55768     },
55769
55770     /**
55771      * Locks the selections.
55772      */
55773     lock : function(){
55774         this.locked = true;
55775     },
55776
55777     /**
55778      * Unlocks the selections.
55779      */
55780     unlock : function(){
55781         this.locked = false;
55782     },
55783
55784     /**
55785      * Returns true if the selections are locked.
55786      * @return {Boolean}
55787      */
55788     isLocked : function(){
55789         return this.locked;
55790     }
55791 });/*
55792  * Based on:
55793  * Ext JS Library 1.1.1
55794  * Copyright(c) 2006-2007, Ext JS, LLC.
55795  *
55796  * Originally Released Under LGPL - original licence link has changed is not relivant.
55797  *
55798  * Fork - LGPL
55799  * <script type="text/javascript">
55800  */
55801 /**
55802  * @extends Roo.grid.AbstractSelectionModel
55803  * @class Roo.grid.RowSelectionModel
55804  * The default SelectionModel used by {@link Roo.grid.Grid}.
55805  * It supports multiple selections and keyboard selection/navigation. 
55806  * @constructor
55807  * @param {Object} config
55808  */
55809 Roo.grid.RowSelectionModel = function(config){
55810     Roo.apply(this, config);
55811     this.selections = new Roo.util.MixedCollection(false, function(o){
55812         return o.id;
55813     });
55814
55815     this.last = false;
55816     this.lastActive = false;
55817
55818     this.addEvents({
55819         /**
55820              * @event selectionchange
55821              * Fires when the selection changes
55822              * @param {SelectionModel} this
55823              */
55824             "selectionchange" : true,
55825         /**
55826              * @event afterselectionchange
55827              * Fires after the selection changes (eg. by key press or clicking)
55828              * @param {SelectionModel} this
55829              */
55830             "afterselectionchange" : true,
55831         /**
55832              * @event beforerowselect
55833              * Fires when a row is selected being selected, return false to cancel.
55834              * @param {SelectionModel} this
55835              * @param {Number} rowIndex The selected index
55836              * @param {Boolean} keepExisting False if other selections will be cleared
55837              */
55838             "beforerowselect" : true,
55839         /**
55840              * @event rowselect
55841              * Fires when a row is selected.
55842              * @param {SelectionModel} this
55843              * @param {Number} rowIndex The selected index
55844              * @param {Roo.data.Record} r The record
55845              */
55846             "rowselect" : true,
55847         /**
55848              * @event rowdeselect
55849              * Fires when a row is deselected.
55850              * @param {SelectionModel} this
55851              * @param {Number} rowIndex The selected index
55852              */
55853         "rowdeselect" : true
55854     });
55855     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55856     this.locked = false;
55857 };
55858
55859 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55860     /**
55861      * @cfg {Boolean} singleSelect
55862      * True to allow selection of only one row at a time (defaults to false)
55863      */
55864     singleSelect : false,
55865
55866     // private
55867     initEvents : function(){
55868
55869         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55870             this.grid.on("mousedown", this.handleMouseDown, this);
55871         }else{ // allow click to work like normal
55872             this.grid.on("rowclick", this.handleDragableRowClick, this);
55873         }
55874
55875         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55876             "up" : function(e){
55877                 if(!e.shiftKey){
55878                     this.selectPrevious(e.shiftKey);
55879                 }else if(this.last !== false && this.lastActive !== false){
55880                     var last = this.last;
55881                     this.selectRange(this.last,  this.lastActive-1);
55882                     this.grid.getView().focusRow(this.lastActive);
55883                     if(last !== false){
55884                         this.last = last;
55885                     }
55886                 }else{
55887                     this.selectFirstRow();
55888                 }
55889                 this.fireEvent("afterselectionchange", this);
55890             },
55891             "down" : function(e){
55892                 if(!e.shiftKey){
55893                     this.selectNext(e.shiftKey);
55894                 }else if(this.last !== false && this.lastActive !== false){
55895                     var last = this.last;
55896                     this.selectRange(this.last,  this.lastActive+1);
55897                     this.grid.getView().focusRow(this.lastActive);
55898                     if(last !== false){
55899                         this.last = last;
55900                     }
55901                 }else{
55902                     this.selectFirstRow();
55903                 }
55904                 this.fireEvent("afterselectionchange", this);
55905             },
55906             scope: this
55907         });
55908
55909         var view = this.grid.view;
55910         view.on("refresh", this.onRefresh, this);
55911         view.on("rowupdated", this.onRowUpdated, this);
55912         view.on("rowremoved", this.onRemove, this);
55913     },
55914
55915     // private
55916     onRefresh : function(){
55917         var ds = this.grid.dataSource, i, v = this.grid.view;
55918         var s = this.selections;
55919         s.each(function(r){
55920             if((i = ds.indexOfId(r.id)) != -1){
55921                 v.onRowSelect(i);
55922             }else{
55923                 s.remove(r);
55924             }
55925         });
55926     },
55927
55928     // private
55929     onRemove : function(v, index, r){
55930         this.selections.remove(r);
55931     },
55932
55933     // private
55934     onRowUpdated : function(v, index, r){
55935         if(this.isSelected(r)){
55936             v.onRowSelect(index);
55937         }
55938     },
55939
55940     /**
55941      * Select records.
55942      * @param {Array} records The records to select
55943      * @param {Boolean} keepExisting (optional) True to keep existing selections
55944      */
55945     selectRecords : function(records, keepExisting){
55946         if(!keepExisting){
55947             this.clearSelections();
55948         }
55949         var ds = this.grid.dataSource;
55950         for(var i = 0, len = records.length; i < len; i++){
55951             this.selectRow(ds.indexOf(records[i]), true);
55952         }
55953     },
55954
55955     /**
55956      * Gets the number of selected rows.
55957      * @return {Number}
55958      */
55959     getCount : function(){
55960         return this.selections.length;
55961     },
55962
55963     /**
55964      * Selects the first row in the grid.
55965      */
55966     selectFirstRow : function(){
55967         this.selectRow(0);
55968     },
55969
55970     /**
55971      * Select the last row.
55972      * @param {Boolean} keepExisting (optional) True to keep existing selections
55973      */
55974     selectLastRow : function(keepExisting){
55975         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55976     },
55977
55978     /**
55979      * Selects the row immediately following the last selected row.
55980      * @param {Boolean} keepExisting (optional) True to keep existing selections
55981      */
55982     selectNext : function(keepExisting){
55983         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55984             this.selectRow(this.last+1, keepExisting);
55985             this.grid.getView().focusRow(this.last);
55986         }
55987     },
55988
55989     /**
55990      * Selects the row that precedes the last selected row.
55991      * @param {Boolean} keepExisting (optional) True to keep existing selections
55992      */
55993     selectPrevious : function(keepExisting){
55994         if(this.last){
55995             this.selectRow(this.last-1, keepExisting);
55996             this.grid.getView().focusRow(this.last);
55997         }
55998     },
55999
56000     /**
56001      * Returns the selected records
56002      * @return {Array} Array of selected records
56003      */
56004     getSelections : function(){
56005         return [].concat(this.selections.items);
56006     },
56007
56008     /**
56009      * Returns the first selected record.
56010      * @return {Record}
56011      */
56012     getSelected : function(){
56013         return this.selections.itemAt(0);
56014     },
56015
56016
56017     /**
56018      * Clears all selections.
56019      */
56020     clearSelections : function(fast){
56021         if(this.locked) return;
56022         if(fast !== true){
56023             var ds = this.grid.dataSource;
56024             var s = this.selections;
56025             s.each(function(r){
56026                 this.deselectRow(ds.indexOfId(r.id));
56027             }, this);
56028             s.clear();
56029         }else{
56030             this.selections.clear();
56031         }
56032         this.last = false;
56033     },
56034
56035
56036     /**
56037      * Selects all rows.
56038      */
56039     selectAll : function(){
56040         if(this.locked) return;
56041         this.selections.clear();
56042         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56043             this.selectRow(i, true);
56044         }
56045     },
56046
56047     /**
56048      * Returns True if there is a selection.
56049      * @return {Boolean}
56050      */
56051     hasSelection : function(){
56052         return this.selections.length > 0;
56053     },
56054
56055     /**
56056      * Returns True if the specified row is selected.
56057      * @param {Number/Record} record The record or index of the record to check
56058      * @return {Boolean}
56059      */
56060     isSelected : function(index){
56061         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56062         return (r && this.selections.key(r.id) ? true : false);
56063     },
56064
56065     /**
56066      * Returns True if the specified record id is selected.
56067      * @param {String} id The id of record to check
56068      * @return {Boolean}
56069      */
56070     isIdSelected : function(id){
56071         return (this.selections.key(id) ? true : false);
56072     },
56073
56074     // private
56075     handleMouseDown : function(e, t){
56076         var view = this.grid.getView(), rowIndex;
56077         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56078             return;
56079         };
56080         if(e.shiftKey && this.last !== false){
56081             var last = this.last;
56082             this.selectRange(last, rowIndex, e.ctrlKey);
56083             this.last = last; // reset the last
56084             view.focusRow(rowIndex);
56085         }else{
56086             var isSelected = this.isSelected(rowIndex);
56087             if(e.button !== 0 && isSelected){
56088                 view.focusRow(rowIndex);
56089             }else if(e.ctrlKey && isSelected){
56090                 this.deselectRow(rowIndex);
56091             }else if(!isSelected){
56092                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56093                 view.focusRow(rowIndex);
56094             }
56095         }
56096         this.fireEvent("afterselectionchange", this);
56097     },
56098     // private
56099     handleDragableRowClick :  function(grid, rowIndex, e) 
56100     {
56101         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56102             this.selectRow(rowIndex, false);
56103             grid.view.focusRow(rowIndex);
56104              this.fireEvent("afterselectionchange", this);
56105         }
56106     },
56107     
56108     /**
56109      * Selects multiple rows.
56110      * @param {Array} rows Array of the indexes of the row to select
56111      * @param {Boolean} keepExisting (optional) True to keep existing selections
56112      */
56113     selectRows : function(rows, keepExisting){
56114         if(!keepExisting){
56115             this.clearSelections();
56116         }
56117         for(var i = 0, len = rows.length; i < len; i++){
56118             this.selectRow(rows[i], true);
56119         }
56120     },
56121
56122     /**
56123      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56124      * @param {Number} startRow The index of the first row in the range
56125      * @param {Number} endRow The index of the last row in the range
56126      * @param {Boolean} keepExisting (optional) True to retain existing selections
56127      */
56128     selectRange : function(startRow, endRow, keepExisting){
56129         if(this.locked) return;
56130         if(!keepExisting){
56131             this.clearSelections();
56132         }
56133         if(startRow <= endRow){
56134             for(var i = startRow; i <= endRow; i++){
56135                 this.selectRow(i, true);
56136             }
56137         }else{
56138             for(var i = startRow; i >= endRow; i--){
56139                 this.selectRow(i, true);
56140             }
56141         }
56142     },
56143
56144     /**
56145      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56146      * @param {Number} startRow The index of the first row in the range
56147      * @param {Number} endRow The index of the last row in the range
56148      */
56149     deselectRange : function(startRow, endRow, preventViewNotify){
56150         if(this.locked) return;
56151         for(var i = startRow; i <= endRow; i++){
56152             this.deselectRow(i, preventViewNotify);
56153         }
56154     },
56155
56156     /**
56157      * Selects a row.
56158      * @param {Number} row The index of the row to select
56159      * @param {Boolean} keepExisting (optional) True to keep existing selections
56160      */
56161     selectRow : function(index, keepExisting, preventViewNotify){
56162         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56163         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56164             if(!keepExisting || this.singleSelect){
56165                 this.clearSelections();
56166             }
56167             var r = this.grid.dataSource.getAt(index);
56168             this.selections.add(r);
56169             this.last = this.lastActive = index;
56170             if(!preventViewNotify){
56171                 this.grid.getView().onRowSelect(index);
56172             }
56173             this.fireEvent("rowselect", this, index, r);
56174             this.fireEvent("selectionchange", this);
56175         }
56176     },
56177
56178     /**
56179      * Deselects a row.
56180      * @param {Number} row The index of the row to deselect
56181      */
56182     deselectRow : function(index, preventViewNotify){
56183         if(this.locked) return;
56184         if(this.last == index){
56185             this.last = false;
56186         }
56187         if(this.lastActive == index){
56188             this.lastActive = false;
56189         }
56190         var r = this.grid.dataSource.getAt(index);
56191         this.selections.remove(r);
56192         if(!preventViewNotify){
56193             this.grid.getView().onRowDeselect(index);
56194         }
56195         this.fireEvent("rowdeselect", this, index);
56196         this.fireEvent("selectionchange", this);
56197     },
56198
56199     // private
56200     restoreLast : function(){
56201         if(this._last){
56202             this.last = this._last;
56203         }
56204     },
56205
56206     // private
56207     acceptsNav : function(row, col, cm){
56208         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56209     },
56210
56211     // private
56212     onEditorKey : function(field, e){
56213         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56214         if(k == e.TAB){
56215             e.stopEvent();
56216             ed.completeEdit();
56217             if(e.shiftKey){
56218                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56219             }else{
56220                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56221             }
56222         }else if(k == e.ENTER && !e.ctrlKey){
56223             e.stopEvent();
56224             ed.completeEdit();
56225             if(e.shiftKey){
56226                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56227             }else{
56228                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56229             }
56230         }else if(k == e.ESC){
56231             ed.cancelEdit();
56232         }
56233         if(newCell){
56234             g.startEditing(newCell[0], newCell[1]);
56235         }
56236     }
56237 });/*
56238  * Based on:
56239  * Ext JS Library 1.1.1
56240  * Copyright(c) 2006-2007, Ext JS, LLC.
56241  *
56242  * Originally Released Under LGPL - original licence link has changed is not relivant.
56243  *
56244  * Fork - LGPL
56245  * <script type="text/javascript">
56246  */
56247 /**
56248  * @class Roo.grid.CellSelectionModel
56249  * @extends Roo.grid.AbstractSelectionModel
56250  * This class provides the basic implementation for cell selection in a grid.
56251  * @constructor
56252  * @param {Object} config The object containing the configuration of this model.
56253  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56254  */
56255 Roo.grid.CellSelectionModel = function(config){
56256     Roo.apply(this, config);
56257
56258     this.selection = null;
56259
56260     this.addEvents({
56261         /**
56262              * @event beforerowselect
56263              * Fires before a cell is selected.
56264              * @param {SelectionModel} this
56265              * @param {Number} rowIndex The selected row index
56266              * @param {Number} colIndex The selected cell index
56267              */
56268             "beforecellselect" : true,
56269         /**
56270              * @event cellselect
56271              * Fires when a cell is selected.
56272              * @param {SelectionModel} this
56273              * @param {Number} rowIndex The selected row index
56274              * @param {Number} colIndex The selected cell index
56275              */
56276             "cellselect" : true,
56277         /**
56278              * @event selectionchange
56279              * Fires when the active selection changes.
56280              * @param {SelectionModel} this
56281              * @param {Object} selection null for no selection or an object (o) with two properties
56282                 <ul>
56283                 <li>o.record: the record object for the row the selection is in</li>
56284                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56285                 </ul>
56286              */
56287             "selectionchange" : true,
56288         /**
56289              * @event tabend
56290              * Fires when the tab (or enter) was pressed on the last editable cell
56291              * You can use this to trigger add new row.
56292              * @param {SelectionModel} this
56293              */
56294             "tabend" : true,
56295          /**
56296              * @event beforeeditnext
56297              * Fires before the next editable sell is made active
56298              * You can use this to skip to another cell or fire the tabend
56299              *    if you set cell to false
56300              * @param {Object} eventdata object : { cell : [ row, col ] } 
56301              */
56302             "beforeeditnext" : true
56303     });
56304     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56305 };
56306
56307 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56308     
56309     enter_is_tab: false,
56310
56311     /** @ignore */
56312     initEvents : function(){
56313         this.grid.on("mousedown", this.handleMouseDown, this);
56314         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56315         var view = this.grid.view;
56316         view.on("refresh", this.onViewChange, this);
56317         view.on("rowupdated", this.onRowUpdated, this);
56318         view.on("beforerowremoved", this.clearSelections, this);
56319         view.on("beforerowsinserted", this.clearSelections, this);
56320         if(this.grid.isEditor){
56321             this.grid.on("beforeedit", this.beforeEdit,  this);
56322         }
56323     },
56324
56325         //private
56326     beforeEdit : function(e){
56327         this.select(e.row, e.column, false, true, e.record);
56328     },
56329
56330         //private
56331     onRowUpdated : function(v, index, r){
56332         if(this.selection && this.selection.record == r){
56333             v.onCellSelect(index, this.selection.cell[1]);
56334         }
56335     },
56336
56337         //private
56338     onViewChange : function(){
56339         this.clearSelections(true);
56340     },
56341
56342         /**
56343          * Returns the currently selected cell,.
56344          * @return {Array} The selected cell (row, column) or null if none selected.
56345          */
56346     getSelectedCell : function(){
56347         return this.selection ? this.selection.cell : null;
56348     },
56349
56350     /**
56351      * Clears all selections.
56352      * @param {Boolean} true to prevent the gridview from being notified about the change.
56353      */
56354     clearSelections : function(preventNotify){
56355         var s = this.selection;
56356         if(s){
56357             if(preventNotify !== true){
56358                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56359             }
56360             this.selection = null;
56361             this.fireEvent("selectionchange", this, null);
56362         }
56363     },
56364
56365     /**
56366      * Returns true if there is a selection.
56367      * @return {Boolean}
56368      */
56369     hasSelection : function(){
56370         return this.selection ? true : false;
56371     },
56372
56373     /** @ignore */
56374     handleMouseDown : function(e, t){
56375         var v = this.grid.getView();
56376         if(this.isLocked()){
56377             return;
56378         };
56379         var row = v.findRowIndex(t);
56380         var cell = v.findCellIndex(t);
56381         if(row !== false && cell !== false){
56382             this.select(row, cell);
56383         }
56384     },
56385
56386     /**
56387      * Selects a cell.
56388      * @param {Number} rowIndex
56389      * @param {Number} collIndex
56390      */
56391     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56392         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56393             this.clearSelections();
56394             r = r || this.grid.dataSource.getAt(rowIndex);
56395             this.selection = {
56396                 record : r,
56397                 cell : [rowIndex, colIndex]
56398             };
56399             if(!preventViewNotify){
56400                 var v = this.grid.getView();
56401                 v.onCellSelect(rowIndex, colIndex);
56402                 if(preventFocus !== true){
56403                     v.focusCell(rowIndex, colIndex);
56404                 }
56405             }
56406             this.fireEvent("cellselect", this, rowIndex, colIndex);
56407             this.fireEvent("selectionchange", this, this.selection);
56408         }
56409     },
56410
56411         //private
56412     isSelectable : function(rowIndex, colIndex, cm){
56413         return !cm.isHidden(colIndex);
56414     },
56415
56416     /** @ignore */
56417     handleKeyDown : function(e){
56418         //Roo.log('Cell Sel Model handleKeyDown');
56419         if(!e.isNavKeyPress()){
56420             return;
56421         }
56422         var g = this.grid, s = this.selection;
56423         if(!s){
56424             e.stopEvent();
56425             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56426             if(cell){
56427                 this.select(cell[0], cell[1]);
56428             }
56429             return;
56430         }
56431         var sm = this;
56432         var walk = function(row, col, step){
56433             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56434         };
56435         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56436         var newCell;
56437
56438       
56439
56440         switch(k){
56441             case e.TAB:
56442                 // handled by onEditorKey
56443                 if (g.isEditor && g.editing) {
56444                     return;
56445                 }
56446                 if(e.shiftKey) {
56447                     newCell = walk(r, c-1, -1);
56448                 } else {
56449                     newCell = walk(r, c+1, 1);
56450                 }
56451                 break;
56452             
56453             case e.DOWN:
56454                newCell = walk(r+1, c, 1);
56455                 break;
56456             
56457             case e.UP:
56458                 newCell = walk(r-1, c, -1);
56459                 break;
56460             
56461             case e.RIGHT:
56462                 newCell = walk(r, c+1, 1);
56463                 break;
56464             
56465             case e.LEFT:
56466                 newCell = walk(r, c-1, -1);
56467                 break;
56468             
56469             case e.ENTER:
56470                 
56471                 if(g.isEditor && !g.editing){
56472                    g.startEditing(r, c);
56473                    e.stopEvent();
56474                    return;
56475                 }
56476                 
56477                 
56478              break;
56479         };
56480         if(newCell){
56481             this.select(newCell[0], newCell[1]);
56482             e.stopEvent();
56483             
56484         }
56485     },
56486
56487     acceptsNav : function(row, col, cm){
56488         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56489     },
56490     /**
56491      * Selects a cell.
56492      * @param {Number} field (not used) - as it's normally used as a listener
56493      * @param {Number} e - event - fake it by using
56494      *
56495      * var e = Roo.EventObjectImpl.prototype;
56496      * e.keyCode = e.TAB
56497      *
56498      * 
56499      */
56500     onEditorKey : function(field, e){
56501         
56502         var k = e.getKey(),
56503             newCell,
56504             g = this.grid,
56505             ed = g.activeEditor,
56506             forward = false;
56507         ///Roo.log('onEditorKey' + k);
56508         
56509         
56510         if (this.enter_is_tab && k == e.ENTER) {
56511             k = e.TAB;
56512         }
56513         
56514         if(k == e.TAB){
56515             if(e.shiftKey){
56516                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56517             }else{
56518                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56519                 forward = true;
56520             }
56521             
56522             e.stopEvent();
56523             
56524         } else if(k == e.ENTER &&  !e.ctrlKey){
56525             ed.completeEdit();
56526             e.stopEvent();
56527             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56528         
56529                 } else if(k == e.ESC){
56530             ed.cancelEdit();
56531         }
56532                 
56533         if (newCell) {
56534             var ecall = { cell : newCell, forward : forward };
56535             this.fireEvent('beforeeditnext', ecall );
56536             newCell = ecall.cell;
56537                         forward = ecall.forward;
56538         }
56539                 
56540         if(newCell){
56541             //Roo.log('next cell after edit');
56542             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56543         } else if (forward) {
56544             // tabbed past last
56545             this.fireEvent.defer(100, this, ['tabend',this]);
56546         }
56547     }
56548 });/*
56549  * Based on:
56550  * Ext JS Library 1.1.1
56551  * Copyright(c) 2006-2007, Ext JS, LLC.
56552  *
56553  * Originally Released Under LGPL - original licence link has changed is not relivant.
56554  *
56555  * Fork - LGPL
56556  * <script type="text/javascript">
56557  */
56558  
56559 /**
56560  * @class Roo.grid.EditorGrid
56561  * @extends Roo.grid.Grid
56562  * Class for creating and editable grid.
56563  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56564  * The container MUST have some type of size defined for the grid to fill. The container will be 
56565  * automatically set to position relative if it isn't already.
56566  * @param {Object} dataSource The data model to bind to
56567  * @param {Object} colModel The column model with info about this grid's columns
56568  */
56569 Roo.grid.EditorGrid = function(container, config){
56570     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56571     this.getGridEl().addClass("xedit-grid");
56572
56573     if(!this.selModel){
56574         this.selModel = new Roo.grid.CellSelectionModel();
56575     }
56576
56577     this.activeEditor = null;
56578
56579         this.addEvents({
56580             /**
56581              * @event beforeedit
56582              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56583              * <ul style="padding:5px;padding-left:16px;">
56584              * <li>grid - This grid</li>
56585              * <li>record - The record being edited</li>
56586              * <li>field - The field name being edited</li>
56587              * <li>value - The value for the field being edited.</li>
56588              * <li>row - The grid row index</li>
56589              * <li>column - The grid column index</li>
56590              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56591              * </ul>
56592              * @param {Object} e An edit event (see above for description)
56593              */
56594             "beforeedit" : true,
56595             /**
56596              * @event afteredit
56597              * Fires after a cell is edited. <br />
56598              * <ul style="padding:5px;padding-left:16px;">
56599              * <li>grid - This grid</li>
56600              * <li>record - The record being edited</li>
56601              * <li>field - The field name being edited</li>
56602              * <li>value - The value being set</li>
56603              * <li>originalValue - The original value for the field, before the edit.</li>
56604              * <li>row - The grid row index</li>
56605              * <li>column - The grid column index</li>
56606              * </ul>
56607              * @param {Object} e An edit event (see above for description)
56608              */
56609             "afteredit" : true,
56610             /**
56611              * @event validateedit
56612              * Fires after a cell is edited, but before the value is set in the record. 
56613          * You can use this to modify the value being set in the field, Return false
56614              * to cancel the change. The edit event object has the following properties <br />
56615              * <ul style="padding:5px;padding-left:16px;">
56616          * <li>editor - This editor</li>
56617              * <li>grid - This grid</li>
56618              * <li>record - The record being edited</li>
56619              * <li>field - The field name being edited</li>
56620              * <li>value - The value being set</li>
56621              * <li>originalValue - The original value for the field, before the edit.</li>
56622              * <li>row - The grid row index</li>
56623              * <li>column - The grid column index</li>
56624              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56625              * </ul>
56626              * @param {Object} e An edit event (see above for description)
56627              */
56628             "validateedit" : true
56629         });
56630     this.on("bodyscroll", this.stopEditing,  this);
56631     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56632 };
56633
56634 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56635     /**
56636      * @cfg {Number} clicksToEdit
56637      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56638      */
56639     clicksToEdit: 2,
56640
56641     // private
56642     isEditor : true,
56643     // private
56644     trackMouseOver: false, // causes very odd FF errors
56645
56646     onCellDblClick : function(g, row, col){
56647         this.startEditing(row, col);
56648     },
56649
56650     onEditComplete : function(ed, value, startValue){
56651         this.editing = false;
56652         this.activeEditor = null;
56653         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56654         var r = ed.record;
56655         var field = this.colModel.getDataIndex(ed.col);
56656         var e = {
56657             grid: this,
56658             record: r,
56659             field: field,
56660             originalValue: startValue,
56661             value: value,
56662             row: ed.row,
56663             column: ed.col,
56664             cancel:false,
56665             editor: ed
56666         };
56667         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56668         cell.show();
56669           
56670         if(String(value) !== String(startValue)){
56671             
56672             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56673                 r.set(field, e.value);
56674                 // if we are dealing with a combo box..
56675                 // then we also set the 'name' colum to be the displayField
56676                 if (ed.field.displayField && ed.field.name) {
56677                     r.set(ed.field.name, ed.field.el.dom.value);
56678                 }
56679                 
56680                 delete e.cancel; //?? why!!!
56681                 this.fireEvent("afteredit", e);
56682             }
56683         } else {
56684             this.fireEvent("afteredit", e); // always fire it!
56685         }
56686         this.view.focusCell(ed.row, ed.col);
56687     },
56688
56689     /**
56690      * Starts editing the specified for the specified row/column
56691      * @param {Number} rowIndex
56692      * @param {Number} colIndex
56693      */
56694     startEditing : function(row, col){
56695         this.stopEditing();
56696         if(this.colModel.isCellEditable(col, row)){
56697             this.view.ensureVisible(row, col, true);
56698           
56699             var r = this.dataSource.getAt(row);
56700             var field = this.colModel.getDataIndex(col);
56701             var cell = Roo.get(this.view.getCell(row,col));
56702             var e = {
56703                 grid: this,
56704                 record: r,
56705                 field: field,
56706                 value: r.data[field],
56707                 row: row,
56708                 column: col,
56709                 cancel:false 
56710             };
56711             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56712                 this.editing = true;
56713                 var ed = this.colModel.getCellEditor(col, row);
56714                 
56715                 if (!ed) {
56716                     return;
56717                 }
56718                 if(!ed.rendered){
56719                     ed.render(ed.parentEl || document.body);
56720                 }
56721                 ed.field.reset();
56722                
56723                 cell.hide();
56724                 
56725                 (function(){ // complex but required for focus issues in safari, ie and opera
56726                     ed.row = row;
56727                     ed.col = col;
56728                     ed.record = r;
56729                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56730                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56731                     this.activeEditor = ed;
56732                     var v = r.data[field];
56733                     ed.startEdit(this.view.getCell(row, col), v);
56734                     // combo's with 'displayField and name set
56735                     if (ed.field.displayField && ed.field.name) {
56736                         ed.field.el.dom.value = r.data[ed.field.name];
56737                     }
56738                     
56739                     
56740                 }).defer(50, this);
56741             }
56742         }
56743     },
56744         
56745     /**
56746      * Stops any active editing
56747      */
56748     stopEditing : function(){
56749         if(this.activeEditor){
56750             this.activeEditor.completeEdit();
56751         }
56752         this.activeEditor = null;
56753     },
56754         
56755          /**
56756      * Called to get grid's drag proxy text, by default returns this.ddText.
56757      * @return {String}
56758      */
56759     getDragDropText : function(){
56760         var count = this.selModel.getSelectedCell() ? 1 : 0;
56761         return String.format(this.ddText, count, count == 1 ? '' : 's');
56762     }
56763         
56764 });/*
56765  * Based on:
56766  * Ext JS Library 1.1.1
56767  * Copyright(c) 2006-2007, Ext JS, LLC.
56768  *
56769  * Originally Released Under LGPL - original licence link has changed is not relivant.
56770  *
56771  * Fork - LGPL
56772  * <script type="text/javascript">
56773  */
56774
56775 // private - not really -- you end up using it !
56776 // This is a support class used internally by the Grid components
56777
56778 /**
56779  * @class Roo.grid.GridEditor
56780  * @extends Roo.Editor
56781  * Class for creating and editable grid elements.
56782  * @param {Object} config any settings (must include field)
56783  */
56784 Roo.grid.GridEditor = function(field, config){
56785     if (!config && field.field) {
56786         config = field;
56787         field = Roo.factory(config.field, Roo.form);
56788     }
56789     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56790     field.monitorTab = false;
56791 };
56792
56793 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56794     
56795     /**
56796      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56797      */
56798     
56799     alignment: "tl-tl",
56800     autoSize: "width",
56801     hideEl : false,
56802     cls: "x-small-editor x-grid-editor",
56803     shim:false,
56804     shadow:"frame"
56805 });/*
56806  * Based on:
56807  * Ext JS Library 1.1.1
56808  * Copyright(c) 2006-2007, Ext JS, LLC.
56809  *
56810  * Originally Released Under LGPL - original licence link has changed is not relivant.
56811  *
56812  * Fork - LGPL
56813  * <script type="text/javascript">
56814  */
56815   
56816
56817   
56818 Roo.grid.PropertyRecord = Roo.data.Record.create([
56819     {name:'name',type:'string'},  'value'
56820 ]);
56821
56822
56823 Roo.grid.PropertyStore = function(grid, source){
56824     this.grid = grid;
56825     this.store = new Roo.data.Store({
56826         recordType : Roo.grid.PropertyRecord
56827     });
56828     this.store.on('update', this.onUpdate,  this);
56829     if(source){
56830         this.setSource(source);
56831     }
56832     Roo.grid.PropertyStore.superclass.constructor.call(this);
56833 };
56834
56835
56836
56837 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56838     setSource : function(o){
56839         this.source = o;
56840         this.store.removeAll();
56841         var data = [];
56842         for(var k in o){
56843             if(this.isEditableValue(o[k])){
56844                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56845             }
56846         }
56847         this.store.loadRecords({records: data}, {}, true);
56848     },
56849
56850     onUpdate : function(ds, record, type){
56851         if(type == Roo.data.Record.EDIT){
56852             var v = record.data['value'];
56853             var oldValue = record.modified['value'];
56854             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56855                 this.source[record.id] = v;
56856                 record.commit();
56857                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56858             }else{
56859                 record.reject();
56860             }
56861         }
56862     },
56863
56864     getProperty : function(row){
56865        return this.store.getAt(row);
56866     },
56867
56868     isEditableValue: function(val){
56869         if(val && val instanceof Date){
56870             return true;
56871         }else if(typeof val == 'object' || typeof val == 'function'){
56872             return false;
56873         }
56874         return true;
56875     },
56876
56877     setValue : function(prop, value){
56878         this.source[prop] = value;
56879         this.store.getById(prop).set('value', value);
56880     },
56881
56882     getSource : function(){
56883         return this.source;
56884     }
56885 });
56886
56887 Roo.grid.PropertyColumnModel = function(grid, store){
56888     this.grid = grid;
56889     var g = Roo.grid;
56890     g.PropertyColumnModel.superclass.constructor.call(this, [
56891         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56892         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56893     ]);
56894     this.store = store;
56895     this.bselect = Roo.DomHelper.append(document.body, {
56896         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56897             {tag: 'option', value: 'true', html: 'true'},
56898             {tag: 'option', value: 'false', html: 'false'}
56899         ]
56900     });
56901     Roo.id(this.bselect);
56902     var f = Roo.form;
56903     this.editors = {
56904         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56905         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56906         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56907         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56908         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56909     };
56910     this.renderCellDelegate = this.renderCell.createDelegate(this);
56911     this.renderPropDelegate = this.renderProp.createDelegate(this);
56912 };
56913
56914 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56915     
56916     
56917     nameText : 'Name',
56918     valueText : 'Value',
56919     
56920     dateFormat : 'm/j/Y',
56921     
56922     
56923     renderDate : function(dateVal){
56924         return dateVal.dateFormat(this.dateFormat);
56925     },
56926
56927     renderBool : function(bVal){
56928         return bVal ? 'true' : 'false';
56929     },
56930
56931     isCellEditable : function(colIndex, rowIndex){
56932         return colIndex == 1;
56933     },
56934
56935     getRenderer : function(col){
56936         return col == 1 ?
56937             this.renderCellDelegate : this.renderPropDelegate;
56938     },
56939
56940     renderProp : function(v){
56941         return this.getPropertyName(v);
56942     },
56943
56944     renderCell : function(val){
56945         var rv = val;
56946         if(val instanceof Date){
56947             rv = this.renderDate(val);
56948         }else if(typeof val == 'boolean'){
56949             rv = this.renderBool(val);
56950         }
56951         return Roo.util.Format.htmlEncode(rv);
56952     },
56953
56954     getPropertyName : function(name){
56955         var pn = this.grid.propertyNames;
56956         return pn && pn[name] ? pn[name] : name;
56957     },
56958
56959     getCellEditor : function(colIndex, rowIndex){
56960         var p = this.store.getProperty(rowIndex);
56961         var n = p.data['name'], val = p.data['value'];
56962         
56963         if(typeof(this.grid.customEditors[n]) == 'string'){
56964             return this.editors[this.grid.customEditors[n]];
56965         }
56966         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56967             return this.grid.customEditors[n];
56968         }
56969         if(val instanceof Date){
56970             return this.editors['date'];
56971         }else if(typeof val == 'number'){
56972             return this.editors['number'];
56973         }else if(typeof val == 'boolean'){
56974             return this.editors['boolean'];
56975         }else{
56976             return this.editors['string'];
56977         }
56978     }
56979 });
56980
56981 /**
56982  * @class Roo.grid.PropertyGrid
56983  * @extends Roo.grid.EditorGrid
56984  * This class represents the  interface of a component based property grid control.
56985  * <br><br>Usage:<pre><code>
56986  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56987       
56988  });
56989  // set any options
56990  grid.render();
56991  * </code></pre>
56992   
56993  * @constructor
56994  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56995  * The container MUST have some type of size defined for the grid to fill. The container will be
56996  * automatically set to position relative if it isn't already.
56997  * @param {Object} config A config object that sets properties on this grid.
56998  */
56999 Roo.grid.PropertyGrid = function(container, config){
57000     config = config || {};
57001     var store = new Roo.grid.PropertyStore(this);
57002     this.store = store;
57003     var cm = new Roo.grid.PropertyColumnModel(this, store);
57004     store.store.sort('name', 'ASC');
57005     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57006         ds: store.store,
57007         cm: cm,
57008         enableColLock:false,
57009         enableColumnMove:false,
57010         stripeRows:false,
57011         trackMouseOver: false,
57012         clicksToEdit:1
57013     }, config));
57014     this.getGridEl().addClass('x-props-grid');
57015     this.lastEditRow = null;
57016     this.on('columnresize', this.onColumnResize, this);
57017     this.addEvents({
57018          /**
57019              * @event beforepropertychange
57020              * Fires before a property changes (return false to stop?)
57021              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57022              * @param {String} id Record Id
57023              * @param {String} newval New Value
57024          * @param {String} oldval Old Value
57025              */
57026         "beforepropertychange": true,
57027         /**
57028              * @event propertychange
57029              * Fires after a property changes
57030              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57031              * @param {String} id Record Id
57032              * @param {String} newval New Value
57033          * @param {String} oldval Old Value
57034              */
57035         "propertychange": true
57036     });
57037     this.customEditors = this.customEditors || {};
57038 };
57039 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57040     
57041      /**
57042      * @cfg {Object} customEditors map of colnames=> custom editors.
57043      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57044      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57045      * false disables editing of the field.
57046          */
57047     
57048       /**
57049      * @cfg {Object} propertyNames map of property Names to their displayed value
57050          */
57051     
57052     render : function(){
57053         Roo.grid.PropertyGrid.superclass.render.call(this);
57054         this.autoSize.defer(100, this);
57055     },
57056
57057     autoSize : function(){
57058         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57059         if(this.view){
57060             this.view.fitColumns();
57061         }
57062     },
57063
57064     onColumnResize : function(){
57065         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57066         this.autoSize();
57067     },
57068     /**
57069      * Sets the data for the Grid
57070      * accepts a Key => Value object of all the elements avaiable.
57071      * @param {Object} data  to appear in grid.
57072      */
57073     setSource : function(source){
57074         this.store.setSource(source);
57075         //this.autoSize();
57076     },
57077     /**
57078      * Gets all the data from the grid.
57079      * @return {Object} data  data stored in grid
57080      */
57081     getSource : function(){
57082         return this.store.getSource();
57083     }
57084 });/*
57085   
57086  * Licence LGPL
57087  
57088  */
57089  
57090 /**
57091  * @class Roo.grid.Calendar
57092  * @extends Roo.util.Grid
57093  * This class extends the Grid to provide a calendar widget
57094  * <br><br>Usage:<pre><code>
57095  var grid = new Roo.grid.Calendar("my-container-id", {
57096      ds: myDataStore,
57097      cm: myColModel,
57098      selModel: mySelectionModel,
57099      autoSizeColumns: true,
57100      monitorWindowResize: false,
57101      trackMouseOver: true
57102      eventstore : real data store..
57103  });
57104  // set any options
57105  grid.render();
57106   
57107   * @constructor
57108  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57109  * The container MUST have some type of size defined for the grid to fill. The container will be
57110  * automatically set to position relative if it isn't already.
57111  * @param {Object} config A config object that sets properties on this grid.
57112  */
57113 Roo.grid.Calendar = function(container, config){
57114         // initialize the container
57115         this.container = Roo.get(container);
57116         this.container.update("");
57117         this.container.setStyle("overflow", "hidden");
57118     this.container.addClass('x-grid-container');
57119
57120     this.id = this.container.id;
57121
57122     Roo.apply(this, config);
57123     // check and correct shorthanded configs
57124     
57125     var rows = [];
57126     var d =1;
57127     for (var r = 0;r < 6;r++) {
57128         
57129         rows[r]=[];
57130         for (var c =0;c < 7;c++) {
57131             rows[r][c]= '';
57132         }
57133     }
57134     if (this.eventStore) {
57135         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57136         this.eventStore.on('load',this.onLoad, this);
57137         this.eventStore.on('beforeload',this.clearEvents, this);
57138          
57139     }
57140     
57141     this.dataSource = new Roo.data.Store({
57142             proxy: new Roo.data.MemoryProxy(rows),
57143             reader: new Roo.data.ArrayReader({}, [
57144                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57145     });
57146
57147     this.dataSource.load();
57148     this.ds = this.dataSource;
57149     this.ds.xmodule = this.xmodule || false;
57150     
57151     
57152     var cellRender = function(v,x,r)
57153     {
57154         return String.format(
57155             '<div class="fc-day  fc-widget-content"><div>' +
57156                 '<div class="fc-event-container"></div>' +
57157                 '<div class="fc-day-number">{0}</div>'+
57158                 
57159                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57160             '</div></div>', v);
57161     
57162     }
57163     
57164     
57165     this.colModel = new Roo.grid.ColumnModel( [
57166         {
57167             xtype: 'ColumnModel',
57168             xns: Roo.grid,
57169             dataIndex : 'weekday0',
57170             header : 'Sunday',
57171             renderer : cellRender
57172         },
57173         {
57174             xtype: 'ColumnModel',
57175             xns: Roo.grid,
57176             dataIndex : 'weekday1',
57177             header : 'Monday',
57178             renderer : cellRender
57179         },
57180         {
57181             xtype: 'ColumnModel',
57182             xns: Roo.grid,
57183             dataIndex : 'weekday2',
57184             header : 'Tuesday',
57185             renderer : cellRender
57186         },
57187         {
57188             xtype: 'ColumnModel',
57189             xns: Roo.grid,
57190             dataIndex : 'weekday3',
57191             header : 'Wednesday',
57192             renderer : cellRender
57193         },
57194         {
57195             xtype: 'ColumnModel',
57196             xns: Roo.grid,
57197             dataIndex : 'weekday4',
57198             header : 'Thursday',
57199             renderer : cellRender
57200         },
57201         {
57202             xtype: 'ColumnModel',
57203             xns: Roo.grid,
57204             dataIndex : 'weekday5',
57205             header : 'Friday',
57206             renderer : cellRender
57207         },
57208         {
57209             xtype: 'ColumnModel',
57210             xns: Roo.grid,
57211             dataIndex : 'weekday6',
57212             header : 'Saturday',
57213             renderer : cellRender
57214         }
57215     ]);
57216     this.cm = this.colModel;
57217     this.cm.xmodule = this.xmodule || false;
57218  
57219         
57220           
57221     //this.selModel = new Roo.grid.CellSelectionModel();
57222     //this.sm = this.selModel;
57223     //this.selModel.init(this);
57224     
57225     
57226     if(this.width){
57227         this.container.setWidth(this.width);
57228     }
57229
57230     if(this.height){
57231         this.container.setHeight(this.height);
57232     }
57233     /** @private */
57234         this.addEvents({
57235         // raw events
57236         /**
57237          * @event click
57238          * The raw click event for the entire grid.
57239          * @param {Roo.EventObject} e
57240          */
57241         "click" : true,
57242         /**
57243          * @event dblclick
57244          * The raw dblclick event for the entire grid.
57245          * @param {Roo.EventObject} e
57246          */
57247         "dblclick" : true,
57248         /**
57249          * @event contextmenu
57250          * The raw contextmenu event for the entire grid.
57251          * @param {Roo.EventObject} e
57252          */
57253         "contextmenu" : true,
57254         /**
57255          * @event mousedown
57256          * The raw mousedown event for the entire grid.
57257          * @param {Roo.EventObject} e
57258          */
57259         "mousedown" : true,
57260         /**
57261          * @event mouseup
57262          * The raw mouseup event for the entire grid.
57263          * @param {Roo.EventObject} e
57264          */
57265         "mouseup" : true,
57266         /**
57267          * @event mouseover
57268          * The raw mouseover event for the entire grid.
57269          * @param {Roo.EventObject} e
57270          */
57271         "mouseover" : true,
57272         /**
57273          * @event mouseout
57274          * The raw mouseout event for the entire grid.
57275          * @param {Roo.EventObject} e
57276          */
57277         "mouseout" : true,
57278         /**
57279          * @event keypress
57280          * The raw keypress event for the entire grid.
57281          * @param {Roo.EventObject} e
57282          */
57283         "keypress" : true,
57284         /**
57285          * @event keydown
57286          * The raw keydown event for the entire grid.
57287          * @param {Roo.EventObject} e
57288          */
57289         "keydown" : true,
57290
57291         // custom events
57292
57293         /**
57294          * @event cellclick
57295          * Fires when a cell is clicked
57296          * @param {Grid} this
57297          * @param {Number} rowIndex
57298          * @param {Number} columnIndex
57299          * @param {Roo.EventObject} e
57300          */
57301         "cellclick" : true,
57302         /**
57303          * @event celldblclick
57304          * Fires when a cell is double clicked
57305          * @param {Grid} this
57306          * @param {Number} rowIndex
57307          * @param {Number} columnIndex
57308          * @param {Roo.EventObject} e
57309          */
57310         "celldblclick" : true,
57311         /**
57312          * @event rowclick
57313          * Fires when a row is clicked
57314          * @param {Grid} this
57315          * @param {Number} rowIndex
57316          * @param {Roo.EventObject} e
57317          */
57318         "rowclick" : true,
57319         /**
57320          * @event rowdblclick
57321          * Fires when a row is double clicked
57322          * @param {Grid} this
57323          * @param {Number} rowIndex
57324          * @param {Roo.EventObject} e
57325          */
57326         "rowdblclick" : true,
57327         /**
57328          * @event headerclick
57329          * Fires when a header is clicked
57330          * @param {Grid} this
57331          * @param {Number} columnIndex
57332          * @param {Roo.EventObject} e
57333          */
57334         "headerclick" : true,
57335         /**
57336          * @event headerdblclick
57337          * Fires when a header cell is double clicked
57338          * @param {Grid} this
57339          * @param {Number} columnIndex
57340          * @param {Roo.EventObject} e
57341          */
57342         "headerdblclick" : true,
57343         /**
57344          * @event rowcontextmenu
57345          * Fires when a row is right clicked
57346          * @param {Grid} this
57347          * @param {Number} rowIndex
57348          * @param {Roo.EventObject} e
57349          */
57350         "rowcontextmenu" : true,
57351         /**
57352          * @event cellcontextmenu
57353          * Fires when a cell is right clicked
57354          * @param {Grid} this
57355          * @param {Number} rowIndex
57356          * @param {Number} cellIndex
57357          * @param {Roo.EventObject} e
57358          */
57359          "cellcontextmenu" : true,
57360         /**
57361          * @event headercontextmenu
57362          * Fires when a header is right clicked
57363          * @param {Grid} this
57364          * @param {Number} columnIndex
57365          * @param {Roo.EventObject} e
57366          */
57367         "headercontextmenu" : true,
57368         /**
57369          * @event bodyscroll
57370          * Fires when the body element is scrolled
57371          * @param {Number} scrollLeft
57372          * @param {Number} scrollTop
57373          */
57374         "bodyscroll" : true,
57375         /**
57376          * @event columnresize
57377          * Fires when the user resizes a column
57378          * @param {Number} columnIndex
57379          * @param {Number} newSize
57380          */
57381         "columnresize" : true,
57382         /**
57383          * @event columnmove
57384          * Fires when the user moves a column
57385          * @param {Number} oldIndex
57386          * @param {Number} newIndex
57387          */
57388         "columnmove" : true,
57389         /**
57390          * @event startdrag
57391          * Fires when row(s) start being dragged
57392          * @param {Grid} this
57393          * @param {Roo.GridDD} dd The drag drop object
57394          * @param {event} e The raw browser event
57395          */
57396         "startdrag" : true,
57397         /**
57398          * @event enddrag
57399          * Fires when a drag operation is complete
57400          * @param {Grid} this
57401          * @param {Roo.GridDD} dd The drag drop object
57402          * @param {event} e The raw browser event
57403          */
57404         "enddrag" : true,
57405         /**
57406          * @event dragdrop
57407          * Fires when dragged row(s) are dropped on a valid DD target
57408          * @param {Grid} this
57409          * @param {Roo.GridDD} dd The drag drop object
57410          * @param {String} targetId The target drag drop object
57411          * @param {event} e The raw browser event
57412          */
57413         "dragdrop" : true,
57414         /**
57415          * @event dragover
57416          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57417          * @param {Grid} this
57418          * @param {Roo.GridDD} dd The drag drop object
57419          * @param {String} targetId The target drag drop object
57420          * @param {event} e The raw browser event
57421          */
57422         "dragover" : true,
57423         /**
57424          * @event dragenter
57425          *  Fires when the dragged row(s) first cross another DD target while being dragged
57426          * @param {Grid} this
57427          * @param {Roo.GridDD} dd The drag drop object
57428          * @param {String} targetId The target drag drop object
57429          * @param {event} e The raw browser event
57430          */
57431         "dragenter" : true,
57432         /**
57433          * @event dragout
57434          * Fires when the dragged row(s) leave another DD target while being dragged
57435          * @param {Grid} this
57436          * @param {Roo.GridDD} dd The drag drop object
57437          * @param {String} targetId The target drag drop object
57438          * @param {event} e The raw browser event
57439          */
57440         "dragout" : true,
57441         /**
57442          * @event rowclass
57443          * Fires when a row is rendered, so you can change add a style to it.
57444          * @param {GridView} gridview   The grid view
57445          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57446          */
57447         'rowclass' : true,
57448
57449         /**
57450          * @event render
57451          * Fires when the grid is rendered
57452          * @param {Grid} grid
57453          */
57454         'render' : true,
57455             /**
57456              * @event select
57457              * Fires when a date is selected
57458              * @param {DatePicker} this
57459              * @param {Date} date The selected date
57460              */
57461         'select': true,
57462         /**
57463              * @event monthchange
57464              * Fires when the displayed month changes 
57465              * @param {DatePicker} this
57466              * @param {Date} date The selected month
57467              */
57468         'monthchange': true,
57469         /**
57470              * @event evententer
57471              * Fires when mouse over an event
57472              * @param {Calendar} this
57473              * @param {event} Event
57474              */
57475         'evententer': true,
57476         /**
57477              * @event eventleave
57478              * Fires when the mouse leaves an
57479              * @param {Calendar} this
57480              * @param {event}
57481              */
57482         'eventleave': true,
57483         /**
57484              * @event eventclick
57485              * Fires when the mouse click an
57486              * @param {Calendar} this
57487              * @param {event}
57488              */
57489         'eventclick': true,
57490         /**
57491              * @event eventrender
57492              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57493              * @param {Calendar} this
57494              * @param {data} data to be modified
57495              */
57496         'eventrender': true
57497         
57498     });
57499
57500     Roo.grid.Grid.superclass.constructor.call(this);
57501     this.on('render', function() {
57502         this.view.el.addClass('x-grid-cal'); 
57503         
57504         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57505
57506     },this);
57507     
57508     if (!Roo.grid.Calendar.style) {
57509         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57510             
57511             
57512             '.x-grid-cal .x-grid-col' :  {
57513                 height: 'auto !important',
57514                 'vertical-align': 'top'
57515             },
57516             '.x-grid-cal  .fc-event-hori' : {
57517                 height: '14px'
57518             }
57519              
57520             
57521         }, Roo.id());
57522     }
57523
57524     
57525     
57526 };
57527 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57528     /**
57529      * @cfg {Store} eventStore The store that loads events.
57530      */
57531     eventStore : 25,
57532
57533      
57534     activeDate : false,
57535     startDay : 0,
57536     autoWidth : true,
57537     monitorWindowResize : false,
57538
57539     
57540     resizeColumns : function() {
57541         var col = (this.view.el.getWidth() / 7) - 3;
57542         // loop through cols, and setWidth
57543         for(var i =0 ; i < 7 ; i++){
57544             this.cm.setColumnWidth(i, col);
57545         }
57546     },
57547      setDate :function(date) {
57548         
57549         Roo.log('setDate?');
57550         
57551         this.resizeColumns();
57552         var vd = this.activeDate;
57553         this.activeDate = date;
57554 //        if(vd && this.el){
57555 //            var t = date.getTime();
57556 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57557 //                Roo.log('using add remove');
57558 //                
57559 //                this.fireEvent('monthchange', this, date);
57560 //                
57561 //                this.cells.removeClass("fc-state-highlight");
57562 //                this.cells.each(function(c){
57563 //                   if(c.dateValue == t){
57564 //                       c.addClass("fc-state-highlight");
57565 //                       setTimeout(function(){
57566 //                            try{c.dom.firstChild.focus();}catch(e){}
57567 //                       }, 50);
57568 //                       return false;
57569 //                   }
57570 //                   return true;
57571 //                });
57572 //                return;
57573 //            }
57574 //        }
57575         
57576         var days = date.getDaysInMonth();
57577         
57578         var firstOfMonth = date.getFirstDateOfMonth();
57579         var startingPos = firstOfMonth.getDay()-this.startDay;
57580         
57581         if(startingPos < this.startDay){
57582             startingPos += 7;
57583         }
57584         
57585         var pm = date.add(Date.MONTH, -1);
57586         var prevStart = pm.getDaysInMonth()-startingPos;
57587 //        
57588         
57589         
57590         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57591         
57592         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57593         //this.cells.addClassOnOver('fc-state-hover');
57594         
57595         var cells = this.cells.elements;
57596         var textEls = this.textNodes;
57597         
57598         //Roo.each(cells, function(cell){
57599         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57600         //});
57601         
57602         days += startingPos;
57603
57604         // convert everything to numbers so it's fast
57605         var day = 86400000;
57606         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57607         //Roo.log(d);
57608         //Roo.log(pm);
57609         //Roo.log(prevStart);
57610         
57611         var today = new Date().clearTime().getTime();
57612         var sel = date.clearTime().getTime();
57613         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57614         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57615         var ddMatch = this.disabledDatesRE;
57616         var ddText = this.disabledDatesText;
57617         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57618         var ddaysText = this.disabledDaysText;
57619         var format = this.format;
57620         
57621         var setCellClass = function(cal, cell){
57622             
57623             //Roo.log('set Cell Class');
57624             cell.title = "";
57625             var t = d.getTime();
57626             
57627             //Roo.log(d);
57628             
57629             
57630             cell.dateValue = t;
57631             if(t == today){
57632                 cell.className += " fc-today";
57633                 cell.className += " fc-state-highlight";
57634                 cell.title = cal.todayText;
57635             }
57636             if(t == sel){
57637                 // disable highlight in other month..
57638                 cell.className += " fc-state-highlight";
57639                 
57640             }
57641             // disabling
57642             if(t < min) {
57643                 //cell.className = " fc-state-disabled";
57644                 cell.title = cal.minText;
57645                 return;
57646             }
57647             if(t > max) {
57648                 //cell.className = " fc-state-disabled";
57649                 cell.title = cal.maxText;
57650                 return;
57651             }
57652             if(ddays){
57653                 if(ddays.indexOf(d.getDay()) != -1){
57654                     // cell.title = ddaysText;
57655                    // cell.className = " fc-state-disabled";
57656                 }
57657             }
57658             if(ddMatch && format){
57659                 var fvalue = d.dateFormat(format);
57660                 if(ddMatch.test(fvalue)){
57661                     cell.title = ddText.replace("%0", fvalue);
57662                    cell.className = " fc-state-disabled";
57663                 }
57664             }
57665             
57666             if (!cell.initialClassName) {
57667                 cell.initialClassName = cell.dom.className;
57668             }
57669             
57670             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57671         };
57672
57673         var i = 0;
57674         
57675         for(; i < startingPos; i++) {
57676             cells[i].dayName =  (++prevStart);
57677             Roo.log(textEls[i]);
57678             d.setDate(d.getDate()+1);
57679             
57680             //cells[i].className = "fc-past fc-other-month";
57681             setCellClass(this, cells[i]);
57682         }
57683         
57684         var intDay = 0;
57685         
57686         for(; i < days; i++){
57687             intDay = i - startingPos + 1;
57688             cells[i].dayName =  (intDay);
57689             d.setDate(d.getDate()+1);
57690             
57691             cells[i].className = ''; // "x-date-active";
57692             setCellClass(this, cells[i]);
57693         }
57694         var extraDays = 0;
57695         
57696         for(; i < 42; i++) {
57697             //textEls[i].innerHTML = (++extraDays);
57698             
57699             d.setDate(d.getDate()+1);
57700             cells[i].dayName = (++extraDays);
57701             cells[i].className = "fc-future fc-other-month";
57702             setCellClass(this, cells[i]);
57703         }
57704         
57705         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57706         
57707         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57708         
57709         // this will cause all the cells to mis
57710         var rows= [];
57711         var i =0;
57712         for (var r = 0;r < 6;r++) {
57713             for (var c =0;c < 7;c++) {
57714                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57715             }    
57716         }
57717         
57718         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57719         for(i=0;i<cells.length;i++) {
57720             
57721             this.cells.elements[i].dayName = cells[i].dayName ;
57722             this.cells.elements[i].className = cells[i].className;
57723             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57724             this.cells.elements[i].title = cells[i].title ;
57725             this.cells.elements[i].dateValue = cells[i].dateValue ;
57726         }
57727         
57728         
57729         
57730         
57731         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57732         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57733         
57734         ////if(totalRows != 6){
57735             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57736            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57737        // }
57738         
57739         this.fireEvent('monthchange', this, date);
57740         
57741         
57742     },
57743  /**
57744      * Returns the grid's SelectionModel.
57745      * @return {SelectionModel}
57746      */
57747     getSelectionModel : function(){
57748         if(!this.selModel){
57749             this.selModel = new Roo.grid.CellSelectionModel();
57750         }
57751         return this.selModel;
57752     },
57753
57754     load: function() {
57755         this.eventStore.load()
57756         
57757         
57758         
57759     },
57760     
57761     findCell : function(dt) {
57762         dt = dt.clearTime().getTime();
57763         var ret = false;
57764         this.cells.each(function(c){
57765             //Roo.log("check " +c.dateValue + '?=' + dt);
57766             if(c.dateValue == dt){
57767                 ret = c;
57768                 return false;
57769             }
57770             return true;
57771         });
57772         
57773         return ret;
57774     },
57775     
57776     findCells : function(rec) {
57777         var s = rec.data.start_dt.clone().clearTime().getTime();
57778        // Roo.log(s);
57779         var e= rec.data.end_dt.clone().clearTime().getTime();
57780        // Roo.log(e);
57781         var ret = [];
57782         this.cells.each(function(c){
57783              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57784             
57785             if(c.dateValue > e){
57786                 return ;
57787             }
57788             if(c.dateValue < s){
57789                 return ;
57790             }
57791             ret.push(c);
57792         });
57793         
57794         return ret;    
57795     },
57796     
57797     findBestRow: function(cells)
57798     {
57799         var ret = 0;
57800         
57801         for (var i =0 ; i < cells.length;i++) {
57802             ret  = Math.max(cells[i].rows || 0,ret);
57803         }
57804         return ret;
57805         
57806     },
57807     
57808     
57809     addItem : function(rec)
57810     {
57811         // look for vertical location slot in
57812         var cells = this.findCells(rec);
57813         
57814         rec.row = this.findBestRow(cells);
57815         
57816         // work out the location.
57817         
57818         var crow = false;
57819         var rows = [];
57820         for(var i =0; i < cells.length; i++) {
57821             if (!crow) {
57822                 crow = {
57823                     start : cells[i],
57824                     end :  cells[i]
57825                 };
57826                 continue;
57827             }
57828             if (crow.start.getY() == cells[i].getY()) {
57829                 // on same row.
57830                 crow.end = cells[i];
57831                 continue;
57832             }
57833             // different row.
57834             rows.push(crow);
57835             crow = {
57836                 start: cells[i],
57837                 end : cells[i]
57838             };
57839             
57840         }
57841         
57842         rows.push(crow);
57843         rec.els = [];
57844         rec.rows = rows;
57845         rec.cells = cells;
57846         for (var i = 0; i < cells.length;i++) {
57847             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57848             
57849         }
57850         
57851         
57852     },
57853     
57854     clearEvents: function() {
57855         
57856         if (!this.eventStore.getCount()) {
57857             return;
57858         }
57859         // reset number of rows in cells.
57860         Roo.each(this.cells.elements, function(c){
57861             c.rows = 0;
57862         });
57863         
57864         this.eventStore.each(function(e) {
57865             this.clearEvent(e);
57866         },this);
57867         
57868     },
57869     
57870     clearEvent : function(ev)
57871     {
57872         if (ev.els) {
57873             Roo.each(ev.els, function(el) {
57874                 el.un('mouseenter' ,this.onEventEnter, this);
57875                 el.un('mouseleave' ,this.onEventLeave, this);
57876                 el.remove();
57877             },this);
57878             ev.els = [];
57879         }
57880     },
57881     
57882     
57883     renderEvent : function(ev,ctr) {
57884         if (!ctr) {
57885              ctr = this.view.el.select('.fc-event-container',true).first();
57886         }
57887         
57888          
57889         this.clearEvent(ev);
57890             //code
57891        
57892         
57893         
57894         ev.els = [];
57895         var cells = ev.cells;
57896         var rows = ev.rows;
57897         this.fireEvent('eventrender', this, ev);
57898         
57899         for(var i =0; i < rows.length; i++) {
57900             
57901             cls = '';
57902             if (i == 0) {
57903                 cls += ' fc-event-start';
57904             }
57905             if ((i+1) == rows.length) {
57906                 cls += ' fc-event-end';
57907             }
57908             
57909             //Roo.log(ev.data);
57910             // how many rows should it span..
57911             var cg = this.eventTmpl.append(ctr,Roo.apply({
57912                 fccls : cls
57913                 
57914             }, ev.data) , true);
57915             
57916             
57917             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57918             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57919             cg.on('click', this.onEventClick, this, ev);
57920             
57921             ev.els.push(cg);
57922             
57923             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57924             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57925             //Roo.log(cg);
57926              
57927             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57928             cg.setWidth(ebox.right - sbox.x -2);
57929         }
57930     },
57931     
57932     renderEvents: function()
57933     {   
57934         // first make sure there is enough space..
57935         
57936         if (!this.eventTmpl) {
57937             this.eventTmpl = new Roo.Template(
57938                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57939                     '<div class="fc-event-inner">' +
57940                         '<span class="fc-event-time">{time}</span>' +
57941                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57942                     '</div>' +
57943                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57944                 '</div>'
57945             );
57946                 
57947         }
57948                
57949         
57950         
57951         this.cells.each(function(c) {
57952             //Roo.log(c.select('.fc-day-content div',true).first());
57953             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57954         });
57955         
57956         var ctr = this.view.el.select('.fc-event-container',true).first();
57957         
57958         var cls;
57959         this.eventStore.each(function(ev){
57960             
57961             this.renderEvent(ev);
57962              
57963              
57964         }, this);
57965         this.view.layout();
57966         
57967     },
57968     
57969     onEventEnter: function (e, el,event,d) {
57970         this.fireEvent('evententer', this, el, event);
57971     },
57972     
57973     onEventLeave: function (e, el,event,d) {
57974         this.fireEvent('eventleave', this, el, event);
57975     },
57976     
57977     onEventClick: function (e, el,event,d) {
57978         this.fireEvent('eventclick', this, el, event);
57979     },
57980     
57981     onMonthChange: function () {
57982         this.store.load();
57983     },
57984     
57985     onLoad: function () {
57986         
57987         //Roo.log('calendar onload');
57988 //         
57989         if(this.eventStore.getCount() > 0){
57990             
57991            
57992             
57993             this.eventStore.each(function(d){
57994                 
57995                 
57996                 // FIXME..
57997                 var add =   d.data;
57998                 if (typeof(add.end_dt) == 'undefined')  {
57999                     Roo.log("Missing End time in calendar data: ");
58000                     Roo.log(d);
58001                     return;
58002                 }
58003                 if (typeof(add.start_dt) == 'undefined')  {
58004                     Roo.log("Missing Start time in calendar data: ");
58005                     Roo.log(d);
58006                     return;
58007                 }
58008                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58009                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58010                 add.id = add.id || d.id;
58011                 add.title = add.title || '??';
58012                 
58013                 this.addItem(d);
58014                 
58015              
58016             },this);
58017         }
58018         
58019         this.renderEvents();
58020     }
58021     
58022
58023 });
58024 /*
58025  grid : {
58026                 xtype: 'Grid',
58027                 xns: Roo.grid,
58028                 listeners : {
58029                     render : function ()
58030                     {
58031                         _this.grid = this;
58032                         
58033                         if (!this.view.el.hasClass('course-timesheet')) {
58034                             this.view.el.addClass('course-timesheet');
58035                         }
58036                         if (this.tsStyle) {
58037                             this.ds.load({});
58038                             return; 
58039                         }
58040                         Roo.log('width');
58041                         Roo.log(_this.grid.view.el.getWidth());
58042                         
58043                         
58044                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58045                             '.course-timesheet .x-grid-row' : {
58046                                 height: '80px'
58047                             },
58048                             '.x-grid-row td' : {
58049                                 'vertical-align' : 0
58050                             },
58051                             '.course-edit-link' : {
58052                                 'color' : 'blue',
58053                                 'text-overflow' : 'ellipsis',
58054                                 'overflow' : 'hidden',
58055                                 'white-space' : 'nowrap',
58056                                 'cursor' : 'pointer'
58057                             },
58058                             '.sub-link' : {
58059                                 'color' : 'green'
58060                             },
58061                             '.de-act-sup-link' : {
58062                                 'color' : 'purple',
58063                                 'text-decoration' : 'line-through'
58064                             },
58065                             '.de-act-link' : {
58066                                 'color' : 'red',
58067                                 'text-decoration' : 'line-through'
58068                             },
58069                             '.course-timesheet .course-highlight' : {
58070                                 'border-top-style': 'dashed !important',
58071                                 'border-bottom-bottom': 'dashed !important'
58072                             },
58073                             '.course-timesheet .course-item' : {
58074                                 'font-family'   : 'tahoma, arial, helvetica',
58075                                 'font-size'     : '11px',
58076                                 'overflow'      : 'hidden',
58077                                 'padding-left'  : '10px',
58078                                 'padding-right' : '10px',
58079                                 'padding-top' : '10px' 
58080                             }
58081                             
58082                         }, Roo.id());
58083                                 this.ds.load({});
58084                     }
58085                 },
58086                 autoWidth : true,
58087                 monitorWindowResize : false,
58088                 cellrenderer : function(v,x,r)
58089                 {
58090                     return v;
58091                 },
58092                 sm : {
58093                     xtype: 'CellSelectionModel',
58094                     xns: Roo.grid
58095                 },
58096                 dataSource : {
58097                     xtype: 'Store',
58098                     xns: Roo.data,
58099                     listeners : {
58100                         beforeload : function (_self, options)
58101                         {
58102                             options.params = options.params || {};
58103                             options.params._month = _this.monthField.getValue();
58104                             options.params.limit = 9999;
58105                             options.params['sort'] = 'when_dt';    
58106                             options.params['dir'] = 'ASC';    
58107                             this.proxy.loadResponse = this.loadResponse;
58108                             Roo.log("load?");
58109                             //this.addColumns();
58110                         },
58111                         load : function (_self, records, options)
58112                         {
58113                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58114                                 // if you click on the translation.. you can edit it...
58115                                 var el = Roo.get(this);
58116                                 var id = el.dom.getAttribute('data-id');
58117                                 var d = el.dom.getAttribute('data-date');
58118                                 var t = el.dom.getAttribute('data-time');
58119                                 //var id = this.child('span').dom.textContent;
58120                                 
58121                                 //Roo.log(this);
58122                                 Pman.Dialog.CourseCalendar.show({
58123                                     id : id,
58124                                     when_d : d,
58125                                     when_t : t,
58126                                     productitem_active : id ? 1 : 0
58127                                 }, function() {
58128                                     _this.grid.ds.load({});
58129                                 });
58130                            
58131                            });
58132                            
58133                            _this.panel.fireEvent('resize', [ '', '' ]);
58134                         }
58135                     },
58136                     loadResponse : function(o, success, response){
58137                             // this is overridden on before load..
58138                             
58139                             Roo.log("our code?");       
58140                             //Roo.log(success);
58141                             //Roo.log(response)
58142                             delete this.activeRequest;
58143                             if(!success){
58144                                 this.fireEvent("loadexception", this, o, response);
58145                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58146                                 return;
58147                             }
58148                             var result;
58149                             try {
58150                                 result = o.reader.read(response);
58151                             }catch(e){
58152                                 Roo.log("load exception?");
58153                                 this.fireEvent("loadexception", this, o, response, e);
58154                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58155                                 return;
58156                             }
58157                             Roo.log("ready...");        
58158                             // loop through result.records;
58159                             // and set this.tdate[date] = [] << array of records..
58160                             _this.tdata  = {};
58161                             Roo.each(result.records, function(r){
58162                                 //Roo.log(r.data);
58163                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58164                                     _this.tdata[r.data.when_dt.format('j')] = [];
58165                                 }
58166                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58167                             });
58168                             
58169                             //Roo.log(_this.tdata);
58170                             
58171                             result.records = [];
58172                             result.totalRecords = 6;
58173                     
58174                             // let's generate some duumy records for the rows.
58175                             //var st = _this.dateField.getValue();
58176                             
58177                             // work out monday..
58178                             //st = st.add(Date.DAY, -1 * st.format('w'));
58179                             
58180                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58181                             
58182                             var firstOfMonth = date.getFirstDayOfMonth();
58183                             var days = date.getDaysInMonth();
58184                             var d = 1;
58185                             var firstAdded = false;
58186                             for (var i = 0; i < result.totalRecords ; i++) {
58187                                 //var d= st.add(Date.DAY, i);
58188                                 var row = {};
58189                                 var added = 0;
58190                                 for(var w = 0 ; w < 7 ; w++){
58191                                     if(!firstAdded && firstOfMonth != w){
58192                                         continue;
58193                                     }
58194                                     if(d > days){
58195                                         continue;
58196                                     }
58197                                     firstAdded = true;
58198                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58199                                     row['weekday'+w] = String.format(
58200                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58201                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58202                                                     d,
58203                                                     date.format('Y-m-')+dd
58204                                                 );
58205                                     added++;
58206                                     if(typeof(_this.tdata[d]) != 'undefined'){
58207                                         Roo.each(_this.tdata[d], function(r){
58208                                             var is_sub = '';
58209                                             var deactive = '';
58210                                             var id = r.id;
58211                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58212                                             if(r.parent_id*1>0){
58213                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58214                                                 id = r.parent_id;
58215                                             }
58216                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58217                                                 deactive = 'de-act-link';
58218                                             }
58219                                             
58220                                             row['weekday'+w] += String.format(
58221                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58222                                                     id, //0
58223                                                     r.product_id_name, //1
58224                                                     r.when_dt.format('h:ia'), //2
58225                                                     is_sub, //3
58226                                                     deactive, //4
58227                                                     desc // 5
58228                                             );
58229                                         });
58230                                     }
58231                                     d++;
58232                                 }
58233                                 
58234                                 // only do this if something added..
58235                                 if(added > 0){ 
58236                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58237                                 }
58238                                 
58239                                 
58240                                 // push it twice. (second one with an hour..
58241                                 
58242                             }
58243                             //Roo.log(result);
58244                             this.fireEvent("load", this, o, o.request.arg);
58245                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58246                         },
58247                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58248                     proxy : {
58249                         xtype: 'HttpProxy',
58250                         xns: Roo.data,
58251                         method : 'GET',
58252                         url : baseURL + '/Roo/Shop_course.php'
58253                     },
58254                     reader : {
58255                         xtype: 'JsonReader',
58256                         xns: Roo.data,
58257                         id : 'id',
58258                         fields : [
58259                             {
58260                                 'name': 'id',
58261                                 'type': 'int'
58262                             },
58263                             {
58264                                 'name': 'when_dt',
58265                                 'type': 'string'
58266                             },
58267                             {
58268                                 'name': 'end_dt',
58269                                 'type': 'string'
58270                             },
58271                             {
58272                                 'name': 'parent_id',
58273                                 'type': 'int'
58274                             },
58275                             {
58276                                 'name': 'product_id',
58277                                 'type': 'int'
58278                             },
58279                             {
58280                                 'name': 'productitem_id',
58281                                 'type': 'int'
58282                             },
58283                             {
58284                                 'name': 'guid',
58285                                 'type': 'int'
58286                             }
58287                         ]
58288                     }
58289                 },
58290                 toolbar : {
58291                     xtype: 'Toolbar',
58292                     xns: Roo,
58293                     items : [
58294                         {
58295                             xtype: 'Button',
58296                             xns: Roo.Toolbar,
58297                             listeners : {
58298                                 click : function (_self, e)
58299                                 {
58300                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58301                                     sd.setMonth(sd.getMonth()-1);
58302                                     _this.monthField.setValue(sd.format('Y-m-d'));
58303                                     _this.grid.ds.load({});
58304                                 }
58305                             },
58306                             text : "Back"
58307                         },
58308                         {
58309                             xtype: 'Separator',
58310                             xns: Roo.Toolbar
58311                         },
58312                         {
58313                             xtype: 'MonthField',
58314                             xns: Roo.form,
58315                             listeners : {
58316                                 render : function (_self)
58317                                 {
58318                                     _this.monthField = _self;
58319                                    // _this.monthField.set  today
58320                                 },
58321                                 select : function (combo, date)
58322                                 {
58323                                     _this.grid.ds.load({});
58324                                 }
58325                             },
58326                             value : (function() { return new Date(); })()
58327                         },
58328                         {
58329                             xtype: 'Separator',
58330                             xns: Roo.Toolbar
58331                         },
58332                         {
58333                             xtype: 'TextItem',
58334                             xns: Roo.Toolbar,
58335                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58336                         },
58337                         {
58338                             xtype: 'Fill',
58339                             xns: Roo.Toolbar
58340                         },
58341                         {
58342                             xtype: 'Button',
58343                             xns: Roo.Toolbar,
58344                             listeners : {
58345                                 click : function (_self, e)
58346                                 {
58347                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58348                                     sd.setMonth(sd.getMonth()+1);
58349                                     _this.monthField.setValue(sd.format('Y-m-d'));
58350                                     _this.grid.ds.load({});
58351                                 }
58352                             },
58353                             text : "Next"
58354                         }
58355                     ]
58356                 },
58357                  
58358             }
58359         };
58360         
58361         *//*
58362  * Based on:
58363  * Ext JS Library 1.1.1
58364  * Copyright(c) 2006-2007, Ext JS, LLC.
58365  *
58366  * Originally Released Under LGPL - original licence link has changed is not relivant.
58367  *
58368  * Fork - LGPL
58369  * <script type="text/javascript">
58370  */
58371  
58372 /**
58373  * @class Roo.LoadMask
58374  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58375  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58376  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58377  * element's UpdateManager load indicator and will be destroyed after the initial load.
58378  * @constructor
58379  * Create a new LoadMask
58380  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58381  * @param {Object} config The config object
58382  */
58383 Roo.LoadMask = function(el, config){
58384     this.el = Roo.get(el);
58385     Roo.apply(this, config);
58386     if(this.store){
58387         this.store.on('beforeload', this.onBeforeLoad, this);
58388         this.store.on('load', this.onLoad, this);
58389         this.store.on('loadexception', this.onLoadException, this);
58390         this.removeMask = false;
58391     }else{
58392         var um = this.el.getUpdateManager();
58393         um.showLoadIndicator = false; // disable the default indicator
58394         um.on('beforeupdate', this.onBeforeLoad, this);
58395         um.on('update', this.onLoad, this);
58396         um.on('failure', this.onLoad, this);
58397         this.removeMask = true;
58398     }
58399 };
58400
58401 Roo.LoadMask.prototype = {
58402     /**
58403      * @cfg {Boolean} removeMask
58404      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58405      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58406      */
58407     /**
58408      * @cfg {String} msg
58409      * The text to display in a centered loading message box (defaults to 'Loading...')
58410      */
58411     msg : 'Loading...',
58412     /**
58413      * @cfg {String} msgCls
58414      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58415      */
58416     msgCls : 'x-mask-loading',
58417
58418     /**
58419      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58420      * @type Boolean
58421      */
58422     disabled: false,
58423
58424     /**
58425      * Disables the mask to prevent it from being displayed
58426      */
58427     disable : function(){
58428        this.disabled = true;
58429     },
58430
58431     /**
58432      * Enables the mask so that it can be displayed
58433      */
58434     enable : function(){
58435         this.disabled = false;
58436     },
58437     
58438     onLoadException : function()
58439     {
58440         Roo.log(arguments);
58441         
58442         if (typeof(arguments[3]) != 'undefined') {
58443             Roo.MessageBox.alert("Error loading",arguments[3]);
58444         } 
58445         /*
58446         try {
58447             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58448                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58449             }   
58450         } catch(e) {
58451             
58452         }
58453         */
58454     
58455         
58456         
58457         this.el.unmask(this.removeMask);
58458     },
58459     // private
58460     onLoad : function()
58461     {
58462         this.el.unmask(this.removeMask);
58463     },
58464
58465     // private
58466     onBeforeLoad : function(){
58467         if(!this.disabled){
58468             this.el.mask(this.msg, this.msgCls);
58469         }
58470     },
58471
58472     // private
58473     destroy : function(){
58474         if(this.store){
58475             this.store.un('beforeload', this.onBeforeLoad, this);
58476             this.store.un('load', this.onLoad, this);
58477             this.store.un('loadexception', this.onLoadException, this);
58478         }else{
58479             var um = this.el.getUpdateManager();
58480             um.un('beforeupdate', this.onBeforeLoad, this);
58481             um.un('update', this.onLoad, this);
58482             um.un('failure', this.onLoad, this);
58483         }
58484     }
58485 };/*
58486  * Based on:
58487  * Ext JS Library 1.1.1
58488  * Copyright(c) 2006-2007, Ext JS, LLC.
58489  *
58490  * Originally Released Under LGPL - original licence link has changed is not relivant.
58491  *
58492  * Fork - LGPL
58493  * <script type="text/javascript">
58494  */
58495
58496
58497 /**
58498  * @class Roo.XTemplate
58499  * @extends Roo.Template
58500  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58501 <pre><code>
58502 var t = new Roo.XTemplate(
58503         '&lt;select name="{name}"&gt;',
58504                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58505         '&lt;/select&gt;'
58506 );
58507  
58508 // then append, applying the master template values
58509  </code></pre>
58510  *
58511  * Supported features:
58512  *
58513  *  Tags:
58514
58515 <pre><code>
58516       {a_variable} - output encoded.
58517       {a_variable.format:("Y-m-d")} - call a method on the variable
58518       {a_variable:raw} - unencoded output
58519       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58520       {a_variable:this.method_on_template(...)} - call a method on the template object.
58521  
58522 </code></pre>
58523  *  The tpl tag:
58524 <pre><code>
58525         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58526         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58527         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58528         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58529   
58530         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58531         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58532 </code></pre>
58533  *      
58534  */
58535 Roo.XTemplate = function()
58536 {
58537     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58538     if (this.html) {
58539         this.compile();
58540     }
58541 };
58542
58543
58544 Roo.extend(Roo.XTemplate, Roo.Template, {
58545
58546     /**
58547      * The various sub templates
58548      */
58549     tpls : false,
58550     /**
58551      *
58552      * basic tag replacing syntax
58553      * WORD:WORD()
58554      *
58555      * // you can fake an object call by doing this
58556      *  x.t:(test,tesT) 
58557      * 
58558      */
58559     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58560
58561     /**
58562      * compile the template
58563      *
58564      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58565      *
58566      */
58567     compile: function()
58568     {
58569         var s = this.html;
58570      
58571         s = ['<tpl>', s, '</tpl>'].join('');
58572     
58573         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58574             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58575             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58576             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58577             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58578             m,
58579             id     = 0,
58580             tpls   = [];
58581     
58582         while(true == !!(m = s.match(re))){
58583             var forMatch   = m[0].match(nameRe),
58584                 ifMatch   = m[0].match(ifRe),
58585                 execMatch   = m[0].match(execRe),
58586                 namedMatch   = m[0].match(namedRe),
58587                 
58588                 exp  = null, 
58589                 fn   = null,
58590                 exec = null,
58591                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58592                 
58593             if (ifMatch) {
58594                 // if - puts fn into test..
58595                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58596                 if(exp){
58597                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58598                 }
58599             }
58600             
58601             if (execMatch) {
58602                 // exec - calls a function... returns empty if true is  returned.
58603                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58604                 if(exp){
58605                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58606                 }
58607             }
58608             
58609             
58610             if (name) {
58611                 // for = 
58612                 switch(name){
58613                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58614                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58615                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58616                 }
58617             }
58618             var uid = namedMatch ? namedMatch[1] : id;
58619             
58620             
58621             tpls.push({
58622                 id:     namedMatch ? namedMatch[1] : id,
58623                 target: name,
58624                 exec:   exec,
58625                 test:   fn,
58626                 body:   m[1] || ''
58627             });
58628             if (namedMatch) {
58629                 s = s.replace(m[0], '');
58630             } else { 
58631                 s = s.replace(m[0], '{xtpl'+ id + '}');
58632             }
58633             ++id;
58634         }
58635         this.tpls = [];
58636         for(var i = tpls.length-1; i >= 0; --i){
58637             this.compileTpl(tpls[i]);
58638             this.tpls[tpls[i].id] = tpls[i];
58639         }
58640         this.master = tpls[tpls.length-1];
58641         return this;
58642     },
58643     /**
58644      * same as applyTemplate, except it's done to one of the subTemplates
58645      * when using named templates, you can do:
58646      *
58647      * var str = pl.applySubTemplate('your-name', values);
58648      *
58649      * 
58650      * @param {Number} id of the template
58651      * @param {Object} values to apply to template
58652      * @param {Object} parent (normaly the instance of this object)
58653      */
58654     applySubTemplate : function(id, values, parent)
58655     {
58656         
58657         
58658         var t = this.tpls[id];
58659         
58660         
58661         try { 
58662             if(t.test && !t.test.call(this, values, parent)){
58663                 return '';
58664             }
58665         } catch(e) {
58666             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58667             Roo.log(e.toString());
58668             Roo.log(t.test);
58669             return ''
58670         }
58671         try { 
58672             
58673             if(t.exec && t.exec.call(this, values, parent)){
58674                 return '';
58675             }
58676         } catch(e) {
58677             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58678             Roo.log(e.toString());
58679             Roo.log(t.exec);
58680             return ''
58681         }
58682         try {
58683             var vs = t.target ? t.target.call(this, values, parent) : values;
58684             parent = t.target ? values : parent;
58685             if(t.target && vs instanceof Array){
58686                 var buf = [];
58687                 for(var i = 0, len = vs.length; i < len; i++){
58688                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58689                 }
58690                 return buf.join('');
58691             }
58692             return t.compiled.call(this, vs, parent);
58693         } catch (e) {
58694             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58695             Roo.log(e.toString());
58696             Roo.log(t.compiled);
58697             return '';
58698         }
58699     },
58700
58701     compileTpl : function(tpl)
58702     {
58703         var fm = Roo.util.Format;
58704         var useF = this.disableFormats !== true;
58705         var sep = Roo.isGecko ? "+" : ",";
58706         var undef = function(str) {
58707             Roo.log("Property not found :"  + str);
58708             return '';
58709         };
58710         
58711         var fn = function(m, name, format, args)
58712         {
58713             //Roo.log(arguments);
58714             args = args ? args.replace(/\\'/g,"'") : args;
58715             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58716             if (typeof(format) == 'undefined') {
58717                 format= 'htmlEncode';
58718             }
58719             if (format == 'raw' ) {
58720                 format = false;
58721             }
58722             
58723             if(name.substr(0, 4) == 'xtpl'){
58724                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58725             }
58726             
58727             // build an array of options to determine if value is undefined..
58728             
58729             // basically get 'xxxx.yyyy' then do
58730             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58731             //    (function () { Roo.log("Property not found"); return ''; })() :
58732             //    ......
58733             
58734             var udef_ar = [];
58735             var lookfor = '';
58736             Roo.each(name.split('.'), function(st) {
58737                 lookfor += (lookfor.length ? '.': '') + st;
58738                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58739             });
58740             
58741             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58742             
58743             
58744             if(format && useF){
58745                 
58746                 args = args ? ',' + args : "";
58747                  
58748                 if(format.substr(0, 5) != "this."){
58749                     format = "fm." + format + '(';
58750                 }else{
58751                     format = 'this.call("'+ format.substr(5) + '", ';
58752                     args = ", values";
58753                 }
58754                 
58755                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58756             }
58757              
58758             if (args.length) {
58759                 // called with xxyx.yuu:(test,test)
58760                 // change to ()
58761                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58762             }
58763             // raw.. - :raw modifier..
58764             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58765             
58766         };
58767         var body;
58768         // branched to use + in gecko and [].join() in others
58769         if(Roo.isGecko){
58770             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58771                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58772                     "';};};";
58773         }else{
58774             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58775             body.push(tpl.body.replace(/(\r\n|\n)/g,
58776                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58777             body.push("'].join('');};};");
58778             body = body.join('');
58779         }
58780         
58781         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58782        
58783         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58784         eval(body);
58785         
58786         return this;
58787     },
58788
58789     applyTemplate : function(values){
58790         return this.master.compiled.call(this, values, {});
58791         //var s = this.subs;
58792     },
58793
58794     apply : function(){
58795         return this.applyTemplate.apply(this, arguments);
58796     }
58797
58798  });
58799
58800 Roo.XTemplate.from = function(el){
58801     el = Roo.getDom(el);
58802     return new Roo.XTemplate(el.value || el.innerHTML);
58803 };